/*******************************************************************************
* Copyright (c) 2006, 2012 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation
*
******************************************************************************/
package org.eclipse.persistence.jpa.internal.jpql;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.eclipse.persistence.jpa.internal.jpql.DeclarationResolver.Declaration;
import org.eclipse.persistence.jpa.internal.jpql.parser.*;
import org.eclipse.persistence.jpa.jpql.ExpressionTools;
import org.eclipse.persistence.jpa.jpql.spi.IJPAVersion;
import static org.eclipse.persistence.jpa.internal.jpql.JPQLQueryProblemMessages.*;
import static org.eclipse.persistence.jpa.internal.jpql.parser.Expression.*;
/**
* This visitor is responsible to gather the problems and warnings found in the query by validating
* it with the JPQL grammar. The semantic is not validated by this visitor.
*
* @see SemanticValidator
*
* @version 2.3
* @since 2.3
* @author Pascal Filion
*/
@SuppressWarnings("nls")
public final class GrammarValidator extends AbstractValidator {
/**
* This validator validates a {@link CollectionExpression} by making sure each item is separated
* by a comma.
*/
private CollectionSeparatedByCommaValidator collectionSeparatedByCommaValidator;
/**
* This validator validates a {@link CollectionExpression} by making sure each item is separated
* by a space.
*/
private CollectionSeparatedBySpaceValidator collectionSeparatedBySpaceValidator;
/**
* This visitor is responsible to retrieve the visited {@link Expression} if it is a
* {@link ComparisonExpression}.
*/
private ComparisonExpressionVisitor comparisonExpressionVisitor;
/**
* This visitor is responsible to traverse the parents of the visited {@link Expression} and
* stops if a parent is {@link FuncExpression}.
*/
private FuncExpressionFinder funcExpressionFinder;
/**
* The registered expression helper mapped by the unique JPQL identifier of the same expression
* to validate.
*/
private Map<String, Object> helpers;
/**
* The cached {@link InputParameter InputParameters} that are present in the entire JPQL query.
*/
private Collection<InputParameter> inputParameters;
/**
* The compiled regular expression that validates numeric literals using {@link #REGULAR_EXPRESSION_NUMERIC_LITERAL}.
*/
private static Pattern numericalLiteralPattern;
/**
* The regular expression of a numeric literal. The possible forms are:
* <pre>
* 2, +2, -2, 2.2, +2.2, -2.2
* 2E10, +2E10, 2E+10, +2E+10
* -2E10, 2E-10, -2E-10
* 2.2E10, +2.2E10, 2.2E+10, +2.2+E10
* -2E10, 2E-10, -2E-10
* -2.2E10, 2.2-E10, -2.2E-10
* 2d, 2D, 2f, 2F
* +2d, +2D, +2f, +2F
* -2d, -2D, -2f, -2F
* 2.2d, 2.2D, 2.2f, 2.2F
* -2.2d, -2.2D, -2.2f, -2.2F
* +2.2d, +2.2D, +2.2f, +2.2F
* </pre>
*/
private static final String REGULAR_EXPRESSION_NUMERIC_LITERAL =
"^[-+]?[0-9]*((\\.[0-9]+([fFdD]|([eE][-+]?[0-9]+))?)|([fFdDlL]|([eE][-+]?[0-9]+)))?$";
/**
* Creates a new <code>GrammarValidator</code>.
*
* @param queryContext The context used to query information about the query
*/
public GrammarValidator(JPQLQueryContext context) {
super(context);
}
private AbstractSingleEncapsulatedExpressionHelper<AbsExpression> absExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<AbsExpression> helper = getHelper(ABS);
if (helper == null) {
helper = buildAbsExpressionHelper();
helpers.put(ABS, helper);
}
return helper;
}
private AbstractSingleEncapsulatedExpressionHelper<AllOrAnyExpression> allOrAnyExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<AllOrAnyExpression> helper = getHelper(ALL);
if (helper == null) {
helper = buildAllOrAnyExpressionHelper();
helpers.put(ALL, helper);
}
return helper;
}
private AbstractSingleEncapsulatedExpressionHelper<AvgFunction> avgFunctionHelper() {
AbstractSingleEncapsulatedExpressionHelper<AvgFunction> helper = getHelper(AVG);
if (helper == null) {
helper = buildAvgFunctionHelper();
helpers.put(AVG, helper);
}
return helper;
}
private AbstractSingleEncapsulatedExpressionHelper<AbsExpression> buildAbsExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<AbsExpression>() {
@Override
public String expressionInvalidKey() {
return AbsExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return AbsExpression_MissingExpression;
}
public String leftParenthesisMissingKey() {
return AbsExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return AbsExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<AllOrAnyExpression> buildAllOrAnyExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<AllOrAnyExpression>() {
@Override
public String[] arguments(AllOrAnyExpression expression) {
return new String[] { expression.getIdentifier() };
}
@Override
public String expressionInvalidKey() {
return AllOrAnyExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return AllOrAnyExpression_MissingExpression;
}
public String leftParenthesisMissingKey() {
return AllOrAnyExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return AllOrAnyExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<AvgFunction> buildAvgFunctionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<AvgFunction>() {
@Override
public String expressionInvalidKey() {
return AvgFunction_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return AvgFunction_MissingExpression;
}
public String leftParenthesisMissingKey() {
return AvgFunction_MissingLeftParenthesis;
}
@Override
public int lengthBeforeEncapsulatedExpression(AvgFunction expression) {
return expression.hasDistinct() ? DISTINCT.length() + (expression.hasSpaceAfterDistinct() ? 1 : 0) :
expression.hasSpaceAfterDistinct() ? 1 : 0;
}
public String rightParenthesisMissingKey() {
return AvgFunction_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<CoalesceExpression> buildCoalesceExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<CoalesceExpression>() {
@Override
public String expressionInvalidKey() {
return CoalesceExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return CoalesceExpression_MissingExpression;
}
public String leftParenthesisMissingKey() {
return CoalesceExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return CoalesceExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<ConcatExpression> buildConcatExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<ConcatExpression>() {
@Override
public String expressionInvalidKey() {
return ConcatExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return ConcatExpression_MissingExpression;
}
@Override
public boolean isValidExpression(ConcatExpression expression) {
// Done in visit(ConcatExpression)
return true;
}
public String leftParenthesisMissingKey() {
return ConcatExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return ConcatExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<CountFunction> buildCountFunctionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<CountFunction>() {
@Override
public String expressionInvalidKey() {
return CountFunction_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return CountFunction_MissingExpression;
}
public String leftParenthesisMissingKey() {
return CountFunction_MissingLeftParenthesis;
}
@Override
public int lengthBeforeEncapsulatedExpression(CountFunction expression) {
return expression.hasDistinct() ? DISTINCT.length() + (expression.hasSpaceAfterDistinct() ? 1 : 0) :
expression.hasSpaceAfterDistinct() ? 1 : 0;
}
public String rightParenthesisMissingKey() {
return CountFunction_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<EntryExpression> buildEntryExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<EntryExpression>() {
@Override
public String expressionInvalidKey() {
return EntryExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return EntryExpression_MissingExpression;
}
@Override
public boolean isValidExpression(EntryExpression expression) {
return isValid(expression.getExpression(), IdentificationVariableBNF.ID);
}
public String leftParenthesisMissingKey() {
return EntryExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return EntryExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<ExistsExpression> buildExistsExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<ExistsExpression>() {
@Override
public String expressionInvalidKey() {
return ExistsExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return ExistsExpression_MissingExpression;
}
public String leftParenthesisMissingKey() {
return ExistsExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return ExistsExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<FuncExpression> buildFuncExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<FuncExpression>() {
@Override
public String expressionInvalidKey() {
return null; // Not used
}
@Override
public String expressionMissingKey() {
return FuncExpression_MissingFunctionName;
}
@Override
public boolean hasExpression(FuncExpression expression) {
// A FUNC expression can have no arguments
return true;
}
@Override
public boolean isValidExpression(FuncExpression expression) {
return true;
}
public String leftParenthesisMissingKey() {
return FuncExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return FuncExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<IndexExpression> buildIndexExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<IndexExpression>() {
@Override
public String expressionInvalidKey() {
return IndexExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return IndexExpression_MissingExpression;
}
@Override
public boolean isValidExpression(IndexExpression expression) {
return isValid(expression.getExpression(), IdentificationVariableBNF.ID);
}
public String leftParenthesisMissingKey() {
return IndexExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return IndexExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<KeyExpression> buildKeyExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<KeyExpression>() {
@Override
public String expressionInvalidKey() {
return KeyExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return KeyExpression_MissingExpression;
}
@Override
public boolean isValidExpression(KeyExpression expression) {
return isValid(expression.getExpression(), IdentificationVariableBNF.ID);
}
public String leftParenthesisMissingKey() {
return KeyExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return KeyExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<LengthExpression> buildLengthExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<LengthExpression>() {
@Override
public String expressionInvalidKey() {
return LengthExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return LengthExpression_MissingExpression;
}
public String leftParenthesisMissingKey() {
return LengthExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return LengthExpression_MissingRightParenthesis;
}
};
}
private AbstractTripleEncapsulatedExpressionHelper<LocateExpression> buildLocateExpressionHelper() {
return new AbstractTripleEncapsulatedExpressionHelper<LocateExpression>() {
@Override
public String firstCommaMissingKey() {
return LocateExpression_MissingFirstComma;
}
@Override
public String firstExpressionInvalidKey() {
return LocateExpression_InvalidFirstExpression;
}
@Override
public String firstExpressionMissingKey() {
return LocateExpression_MissingFirstExpression;
}
public String identifier(LocateExpression expression) {
return LOCATE;
}
@Override
public boolean isFirstExpressionValid(LocateExpression expression) {
return isValid(expression.getFirstExpression(), StringPrimaryBNF.ID);
}
@Override
public boolean isSecondExpressionValid(LocateExpression expression) {
return isValid(expression.getSecondExpression(), StringPrimaryBNF.ID);
}
@Override
public boolean isThirdExpressionValid(LocateExpression expression) {
return isValid(expression.getThirdExpression(), StringPrimaryBNF.ID);
}
public String leftParenthesisMissingKey() {
return LocateExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return LocateExpression_MissingRightParenthesis;
}
@Override
public String secondCommaMissingKey() {
return LocateExpression_MissingSecondComma;
}
@Override
public String secondExpressionInvalidKey() {
return LocateExpression_InvalidSecondExpression;
}
@Override
public String secondExpressionMissingKey() {
return LocateExpression_MissingSecondExpression;
}
@Override
public String thirdExpressionInvalidKey() {
return LocateExpression_InvalidThirdExpression;
}
@Override
public String thirdExpressionMissingKey() {
return LocateExpression_MissingThirdExpression;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<LowerExpression> buildLowerExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<LowerExpression>() {
@Override
public String expressionInvalidKey() {
return LowerExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return LowerExpression_MissingExpression;
}
public String leftParenthesisMissingKey() {
return LowerExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return LowerExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<MaxFunction> buildMaxFunctionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<MaxFunction>() {
@Override
public String expressionInvalidKey() {
return MaxFunction_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return MaxFunction_MissingExpression;
}
public String leftParenthesisMissingKey() {
return MaxFunction_MissingLeftParenthesis;
}
@Override
public int lengthBeforeEncapsulatedExpression(MaxFunction expression) {
return expression.hasDistinct() ? DISTINCT.length() + (expression.hasSpaceAfterDistinct() ? 1 : 0) :
expression.hasSpaceAfterDistinct() ? 1 : 0;
}
public String rightParenthesisMissingKey() {
return MaxFunction_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<MinFunction> buildMinFunctionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<MinFunction>() {
@Override
public String expressionInvalidKey() {
return MinFunction_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return MinFunction_MissingExpression;
}
public String leftParenthesisMissingKey() {
return MinFunction_MissingLeftParenthesis;
}
@Override
public int lengthBeforeEncapsulatedExpression(MinFunction expression) {
return expression.hasDistinct() ? DISTINCT.length() + (expression.hasSpaceAfterDistinct() ? 1 : 0) :
expression.hasSpaceAfterDistinct() ? 1 : 0;
}
public String rightParenthesisMissingKey() {
return MinFunction_MissingRightParenthesis;
}
};
}
private AbstractDoubleEncapsulatedExpressionHelper<ModExpression> buildModExpressionHelper() {
return new AbstractDoubleEncapsulatedExpressionHelper<ModExpression>() {
@Override
public String firstExpressionInvalidKey() {
return ModExpression_InvalidFirstExpression;
}
@Override
public String firstExpressionMissingKey() {
return ModExpression_MissingFirstExpression;
}
public String identifier(ModExpression expression) {
return MOD;
}
@Override
public boolean isFirstExpressionValid(ModExpression expression) {
return isValid(expression.getFirstExpression(), SimpleArithmeticExpressionBNF.ID);
}
@Override
public boolean isSecondExpressionValid(ModExpression expression) {
return isValid(expression.getSecondExpression(), SimpleArithmeticExpressionBNF.ID);
}
public String leftParenthesisMissingKey() {
return ModExpression_MissingLeftParenthesis;
}
@Override
public String missingCommaKey() {
return ModExpression_MissingComma;
}
public String rightParenthesisMissingKey() {
return ModExpression_MissingRightParenthesis;
}
@Override
public String secondExpressionInvalidKey() {
return ModExpression_InvalidSecondParenthesis;
}
@Override
public String secondExpressionMissingKey() {
return ModExpression_MissingSecondExpression;
}
};
}
private AbstractDoubleEncapsulatedExpressionHelper<NullIfExpression> buildNullIfExpressionHelper() {
return new AbstractDoubleEncapsulatedExpressionHelper<NullIfExpression>() {
@Override
public String firstExpressionInvalidKey() {
return NullIfExpression_InvalidFirstExpression;
}
@Override
public String firstExpressionMissingKey() {
return NullIfExpression_MissingFirstExpression;
}
public String identifier(NullIfExpression expression) {
return NULLIF;
}
@Override
public boolean isFirstExpressionValid(NullIfExpression expression) {
return isValid(expression.getFirstExpression(), ScalarExpressionBNF.ID);
}
@Override
public boolean isSecondExpressionValid(NullIfExpression expression) {
return isValid(expression.getSecondExpression(), ScalarExpressionBNF.ID);
}
public String leftParenthesisMissingKey() {
return NullIfExpression_MissingLeftParenthesis;
}
@Override
public String missingCommaKey() {
return NullIfExpression_MissingComma;
}
public String rightParenthesisMissingKey() {
return NullIfExpression_MissingRightParenthesis;
}
@Override
public String secondExpressionInvalidKey() {
return NullIfExpression_InvalidSecondExpression;
}
@Override
public String secondExpressionMissingKey() {
return NullIfExpression_MissingSecondExpression;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<ObjectExpression> buildObjectExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<ObjectExpression>() {
@Override
public String expressionInvalidKey() {
return ObjectExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return ObjectExpression_MissingExpression;
}
@Override
public boolean isValidExpression(ObjectExpression expression) {
return isValid(expression.getExpression(), IdentificationVariableBNF.ID);
}
public String leftParenthesisMissingKey() {
return ObjectExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return ObjectExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<SizeExpression> buildSizeExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<SizeExpression>() {
@Override
public String expressionInvalidKey() {
return SizeExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return SizeExpression_MissingExpression;
}
public String leftParenthesisMissingKey() {
return SizeExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return SizeExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<SqrtExpression> buildSqrtExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<SqrtExpression>() {
@Override
public String expressionInvalidKey() {
return SqrtExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return SqrtExpression_MissingExpression;
}
public String leftParenthesisMissingKey() {
return SqrtExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return SqrtExpression_MissingRightParenthesis;
}
};
}
private AbstractTripleEncapsulatedExpressionHelper<SubstringExpression> buildSubstringExpressionHelper() {
return new AbstractTripleEncapsulatedExpressionHelper<SubstringExpression>() {
@Override
public String firstCommaMissingKey() {
return SubstringExpression_MissingFirstComma;
}
@Override
public String firstExpressionInvalidKey() {
return SubstringExpression_InvalidFirstExpression;
}
@Override
public String firstExpressionMissingKey() {
return SubstringExpression_MissingFirstExpression;
}
@Override
public boolean hasThirdExpression(SubstringExpression expression) {
boolean hasThirdExpression = super.hasThirdExpression(expression);
// If there is no third expression and the JPA version is 2.0, then we trick the
// validator to think there is an expression because it is optional
if (!hasThirdExpression && getJPAVersion().isNewerThanOrEqual(IJPAVersion.VERSION_2_0)) {
hasThirdExpression = true;
}
return hasThirdExpression;
}
public String identifier(SubstringExpression expression) {
return SUBSTRING;
}
@Override
public boolean isFirstExpressionValid(SubstringExpression expression) {
return isValid(expression.getFirstExpression(), SimpleArithmeticExpressionBNF.ID);
}
@Override
public boolean isSecondExpressionValid(SubstringExpression expression) {
return isValid(expression.getSecondExpression(), SimpleArithmeticExpressionBNF.ID);
}
@Override
public boolean isThirdExpressionValid(SubstringExpression expression) {
return isValid(expression.getThirdExpression(), SimpleArithmeticExpressionBNF.ID);
}
public String leftParenthesisMissingKey() {
return SubstringExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return SubstringExpression_MissingRightParenthesis;
}
@Override
public String secondCommaMissingKey() {
return SubstringExpression_MissingSecondComma;
}
@Override
public String secondExpressionInvalidKey() {
return SubstringExpression_InvalidSecondExpression;
}
@Override
public String secondExpressionMissingKey() {
return SubstringExpression_MissingSecondExpression;
}
@Override
public String thirdExpressionInvalidKey() {
return SubstringExpression_InvalidThirdExpression;
}
@Override
public String thirdExpressionMissingKey() {
return SubstringExpression_MissingThirdExpression;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<SumFunction> buildSumFunctionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<SumFunction>() {
@Override
public String expressionInvalidKey() {
return SumFunction_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return SumFunction_MissingExpression;
}
public String leftParenthesisMissingKey() {
return SumFunction_MissingLeftParenthesis;
}
@Override
public int lengthBeforeEncapsulatedExpression(SumFunction expression) {
return expression.hasDistinct() ? DISTINCT.length() + (expression.hasSpaceAfterDistinct() ? 1 : 0) :
expression.hasSpaceAfterDistinct() ? 1 : 0;
}
public String rightParenthesisMissingKey() {
return SumFunction_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<TrimExpression> buildTrimExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<TrimExpression>() {
@Override
public String expressionInvalidKey() {
return TrimExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return TrimExpression_MissingExpression;
}
@Override
public boolean hasExpression(TrimExpression expression) {
return true;
}
@Override
public boolean isValidExpression(TrimExpression expression) {
// Done outside of this helper
return true;
}
public String leftParenthesisMissingKey() {
return TrimExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return TrimExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<TypeExpression> buildTypeExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<TypeExpression>() {
@Override
public String expressionInvalidKey() {
return TypeExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return TypeExpression_MissingExpression;
}
public String leftParenthesisMissingKey() {
return TypeExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return TypeExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<UpperExpression> buildUpperExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<UpperExpression>() {
@Override
public String expressionInvalidKey() {
return UpperExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return UpperExpression_MissingExpression;
}
public String leftParenthesisMissingKey() {
return UpperExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return UpperExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<ValueExpression> buildValueExpressionHelper() {
return new AbstractSingleEncapsulatedExpressionHelper<ValueExpression>() {
@Override
public String expressionInvalidKey() {
return ValueExpression_InvalidExpression;
}
@Override
public String expressionMissingKey() {
return ValueExpression_MissingExpression;
}
@Override
public boolean isValidExpression(ValueExpression expression) {
return isValid(expression.getExpression(), IdentificationVariableBNF.ID);
}
public String leftParenthesisMissingKey() {
return ValueExpression_MissingLeftParenthesis;
}
public String rightParenthesisMissingKey() {
return ValueExpression_MissingRightParenthesis;
}
};
}
private AbstractSingleEncapsulatedExpressionHelper<CoalesceExpression> coalesceExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<CoalesceExpression> helper = getHelper(COALESCE);
if (helper == null) {
helper = buildCoalesceExpressionHelper();
helpers.put(COALESCE, helper);
}
return helper;
}
private CollectionSeparatedByCommaValidator collectionSeparatedByCommaValidator() {
if (collectionSeparatedByCommaValidator == null) {
collectionSeparatedByCommaValidator = new CollectionSeparatedByCommaValidator();
}
return collectionSeparatedByCommaValidator;
}
private CollectionSeparatedBySpaceValidator collectionSeparatedBySpaceValidator() {
if (collectionSeparatedBySpaceValidator == null) {
collectionSeparatedBySpaceValidator = new CollectionSeparatedBySpaceValidator();
}
return collectionSeparatedBySpaceValidator;
}
private ComparisonExpressionVisitor comparisonExpressionVisitor() {
if (comparisonExpressionVisitor == null) {
comparisonExpressionVisitor = new ComparisonExpressionVisitor();
}
return comparisonExpressionVisitor;
}
private AbstractSingleEncapsulatedExpressionHelper<ConcatExpression> concatExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<ConcatExpression> helper = getHelper(CONCAT);
if (helper == null) {
helper = buildConcatExpressionHelper();
helpers.put(CONCAT, helper);
}
return helper;
}
private AbstractSingleEncapsulatedExpressionHelper<CountFunction> countFunctionHelper() {
AbstractSingleEncapsulatedExpressionHelper<CountFunction> helper = getHelper(COUNT);
if (helper == null) {
helper = buildCountFunctionHelper();
helpers.put(COUNT, helper);
}
return helper;
}
/**
* {@inheritDoc}
*/
@Override
public void dispose() {
super.dispose();
inputParameters.clear();
}
private AbstractSingleEncapsulatedExpressionHelper<EntryExpression> entryExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<EntryExpression> helper = getHelper(ENTRY);
if (helper == null) {
helper = buildEntryExpressionHelper();
helpers.put(ENTRY, helper);
}
return helper;
}
private AbstractSingleEncapsulatedExpressionHelper<ExistsExpression> existsExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<ExistsExpression> helper = getHelper(EXISTS);
if (helper == null) {
helper = buildExistsExpressionHelper();
helpers.put(EXISTS, helper);
}
return helper;
}
private FuncExpressionFinder funcExpressionFinder() {
if (funcExpressionFinder == null) {
funcExpressionFinder = new FuncExpressionFinder();
}
return funcExpressionFinder;
}
private AbstractSingleEncapsulatedExpressionHelper<FuncExpression> funcExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<FuncExpression> helper = getHelper(FUNC);
if (helper == null) {
helper = buildFuncExpressionHelper();
helpers.put(FUNC, helper);
}
return helper;
}
@SuppressWarnings("unchecked")
private <T> T getHelper(String identifier) {
return (T) helpers.get(identifier);
}
private AbstractSingleEncapsulatedExpressionHelper<IndexExpression> indexExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<IndexExpression> helper = getHelper(INDEX);
if (helper == null) {
helper = buildIndexExpressionHelper();
helpers.put(INDEX, helper);
}
return helper;
}
/**
* {@inheritDoc}
*/
@Override
protected void initialize() {
super.initialize();
helpers = new HashMap<String, Object>();
inputParameters = new ArrayList<InputParameter>();
}
private boolean isChildOfComparisonExpession(AllOrAnyExpression expression) {
ComparisonExpressionVisitor visitor = comparisonExpressionVisitor();
BypassParentSubExpressionVisitor bypassVisitor = bypassParentSubExpressionVisitor();
try {
bypassVisitor.visitor = visitor;
expression.getParent().accept(visitor);
return visitor.expression != null;
}
finally {
bypassVisitor.visitor = null;
visitor.expression = null;
}
}
private boolean isIdentificationVariableDeclaredBefore(String variableName,
int variableNameIndex,
int joinIndex,
List<Declaration> declarations) {
// Stop before variableNameIndex if the variableName is not in a JOIN expression (which means
// joinIndex is -1), otherwise stop at variableNameIndex to verify the range variable name
// or the JOIN expression before joinIndex
boolean scanDeclaration = (joinIndex > -1);
for (int index = 0; scanDeclaration ? (index <= variableNameIndex) : (index < variableNameIndex); index++) {
Declaration declaration = declarations.get(index);
String previousVariableName = declaration.getVariableName();
if (variableName.equalsIgnoreCase(previousVariableName)) {
return true;
}
if (declaration.hasJoins()) {
List<Map.Entry<Join, String>> joinEntries = declaration.getJoinEntries();
// Scan all the JOIN expression if index is not variableNameIndex, otherwise
// scan up to joinIndex, exclusively
int endIndex = (index == variableNameIndex) ? joinIndex : joinEntries.size();
for (int subIndex = 0; subIndex < endIndex; subIndex++) {
Map.Entry<Join, String> join = joinEntries.get(subIndex);
previousVariableName = join.getValue();
if (variableName.equalsIgnoreCase(previousVariableName)) {
return true;
}
}
}
}
return false;
}
private boolean isInFuncExpressionInSelectClause(OwningClauseVisitor visitor,
InputParameter expression) {
// If the owning clause is not a SELECT clause, then ignore the check
if (visitor.selectClause == null &&
visitor.simpleSelectClause == null) {
return false;
}
FuncExpressionFinder finder = funcExpressionFinder();
try {
expression.accept(finder);
return finder.expression != null;
}
finally {
finder.expression = null;
}
}
private boolean isNumericLiteral(String text) {
return numericalLiteralPattern().matcher(text).matches();
}
private boolean isRightParenthesisMissing(AbstractTripleEncapsulatedExpression expression) {
if (!expression.hasLeftParenthesis() ||
!expression.hasFirstExpression() ||
expression.hasRightParenthesis()) {
return false;
}
if (expression.hasFirstExpression() &&
!expression.hasFirstComma() &&
!expression.hasSecondExpression() &&
!expression.hasSecondComma() &&
!expression.hasThirdExpression()) {
return false;
}
if (expression.hasFirstComma() &&
!expression.hasSecondExpression() &&
!expression.hasSecondComma() &&
!expression.hasThirdExpression()) {
return false;
}
if (expression.hasSecondExpression() &&
expression.hasSecondComma() &&
!expression.hasThirdExpression()) {
return false;
}
return true;
}
private boolean isValidJPA20Identifier() {
// EclipseLink always parses JPQL queries using the latest JPA version
// TODO: What about EclipseLink platform older and that doesn't support JPA 2.0?
return isEclipseLinkPlatform() || getJPAVersion().isNewerThanOrEqual(IJPAVersion.VERSION_2_0);
}
private AbstractSingleEncapsulatedExpressionHelper<KeyExpression> keyExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<KeyExpression> helper = getHelper(KEY);
if (helper == null) {
helper = buildKeyExpressionHelper();
helpers.put(KEY, helper);
}
return helper;
}
private AbstractSingleEncapsulatedExpressionHelper<LengthExpression> lengthExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<LengthExpression> helper = getHelper(LENGTH);
if (helper == null) {
helper = buildLengthExpressionHelper();
helpers.put(LENGTH, helper);
}
return helper;
}
private AbstractTripleEncapsulatedExpressionHelper<LocateExpression> locateExpressionHelper() {
AbstractTripleEncapsulatedExpressionHelper<LocateExpression> helper = getHelper(LOCATE);
if (helper == null) {
helper = buildLocateExpressionHelper();
helpers.put(LOCATE, helper);
}
return helper;
}
private AbstractSingleEncapsulatedExpressionHelper<LowerExpression> lowerExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<LowerExpression> helper = getHelper(LOWER);
if (helper == null) {
helper = buildLowerExpressionHelper();
helpers.put(LOWER, helper);
}
return helper;
}
private AbstractSingleEncapsulatedExpressionHelper<MaxFunction> maxFunctionHelper() {
AbstractSingleEncapsulatedExpressionHelper<MaxFunction> helper = getHelper(MAX);
if (helper == null) {
helper = buildMaxFunctionHelper();
helpers.put(MAX, helper);
}
return helper;
}
private AbstractSingleEncapsulatedExpressionHelper<MinFunction> minFunctionHelper() {
AbstractSingleEncapsulatedExpressionHelper<MinFunction> helper = getHelper(MIN);
if (helper == null) {
helper = buildMinFunctionHelper();
helpers.put(MIN, helper);
}
return helper;
}
private AbstractDoubleEncapsulatedExpressionHelper<ModExpression> modExpressionHelper() {
AbstractDoubleEncapsulatedExpressionHelper<ModExpression> helper = getHelper(MOD);
if (helper == null) {
helper = buildModExpressionHelper();
helpers.put(MOD, helper);
}
return helper;
}
private AbstractDoubleEncapsulatedExpressionHelper<NullIfExpression> nullIfExpressionHelper() {
AbstractDoubleEncapsulatedExpressionHelper<NullIfExpression> helper = getHelper(NULLIF);
if (helper == null) {
helper = buildNullIfExpressionHelper();
helpers.put(NULLIF, helper);
}
return helper;
}
private Pattern numericalLiteralPattern() {
if (numericalLiteralPattern == null) {
numericalLiteralPattern = Pattern.compile(REGULAR_EXPRESSION_NUMERIC_LITERAL);
}
return numericalLiteralPattern;
}
private AbstractSingleEncapsulatedExpressionHelper<ObjectExpression> objectExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<ObjectExpression> helper = getHelper(OBJECT);
if (helper == null) {
helper = buildObjectExpressionHelper();
helpers.put(OBJECT, helper);
}
return helper;
}
private AbstractSingleEncapsulatedExpressionHelper<SizeExpression> sizeExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<SizeExpression> helper = getHelper(SIZE);
if (helper == null) {
helper = buildSizeExpressionHelper();
helpers.put(SIZE, helper);
}
return helper;
}
private AbstractSingleEncapsulatedExpressionHelper<SqrtExpression> sqrtExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<SqrtExpression> helper = getHelper(SQRT);
if (helper == null) {
helper = buildSqrtExpressionHelper();
helpers.put(SQRT, helper);
}
return helper;
}
private AbstractTripleEncapsulatedExpressionHelper<SubstringExpression> substringExpressionHelper() {
AbstractTripleEncapsulatedExpressionHelper<SubstringExpression> helper = getHelper(SUBSTRING);
if (helper == null) {
helper = buildSubstringExpressionHelper();
helpers.put(SUBSTRING, helper);
}
return helper;
}
private AbstractSingleEncapsulatedExpressionHelper<SumFunction> sumFunctionHelper() {
AbstractSingleEncapsulatedExpressionHelper<SumFunction> helper = getHelper(SUM);
if (helper == null) {
helper = buildSumFunctionHelper();
helpers.put(SUM, helper);
}
return helper;
}
private AbstractSingleEncapsulatedExpressionHelper<TrimExpression> trimExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<TrimExpression> helper = getHelper(TRIM);
if (helper == null) {
helper = buildTrimExpressionHelper();
helpers.put(TRIM, helper);
}
return helper;
}
private AbstractSingleEncapsulatedExpressionHelper<TypeExpression> typeExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<TypeExpression> helper = getHelper(TYPE);
if (helper == null) {
helper = buildTypeExpressionHelper();
helpers.put(TYPE, helper);
}
return helper;
}
private AbstractSingleEncapsulatedExpressionHelper<UpperExpression> upperExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<UpperExpression> helper = getHelper(UPPER);
if (helper == null) {
helper = buildUpperExpressionHelper();
helpers.put(UPPER, helper);
}
return helper;
}
private void validateAbstractConditionalClause(AbstractConditionalClause expression,
String missingConditionalExpressionMessageKey,
String invalidConditionalExpressionMessageKey) {
// Missing conditional expression
if (!expression.hasConditionalExpression()) {
int startPosition = position(expression);
int endPosition = startPosition + expression.getIdentifier().length();
if (expression.hasSpaceAfterIdentifier()) {
endPosition++;
}
addProblem(expression, startPosition, endPosition, missingConditionalExpressionMessageKey);
}
// Invalid conditional expression
else {
Expression conditionalExpression = expression.getConditionalExpression();
if (!isValid(conditionalExpression, ConditionalExpressionBNF.ID)) {
int startPosition = position(conditionalExpression);
int endPosition = startPosition + length(conditionalExpression);
addProblem(expression, startPosition, endPosition, invalidConditionalExpressionMessageKey);
}
}
}
private <T extends AbstractDoubleEncapsulatedExpression>
void validateAbstractDoubleEncapsulatedExpression
(T expression, AbstractDoubleEncapsulatedExpressionHelper<T> helper) {
String identifier = helper.identifier(expression);
// Missing '('
if (!expression.hasLeftParenthesis()) {
int startPosition = position(expression) + identifier.length();
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, helper.leftParenthesisMissingKey());
}
// Missing ')'
if (expression.hasLeftParenthesis() &&
helper.hasSecondExpression(expression) &&
!expression.hasRightParenthesis()) {
int startPosition = position(expression) + identifier.length() + 1 /* '(' */;
// First expression
if (helper.hasFirstExpression(expression)) {
startPosition += length(expression.getFirstExpression());
}
if (expression.hasComma()) {
startPosition++;
}
if (expression.hasSpaceAfterComma()) {
startPosition++;
}
// Second expression
if (helper.hasSecondExpression(expression)) {
startPosition += length(expression.getSecondExpression());
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, helper.rightParenthesisMissingKey());
}
if (expression.hasLeftParenthesis()) {
// Missing first expression
if (!helper.hasFirstExpression(expression)) {
int startPosition = position(expression) + identifier.length() + 1 /* '(' */;
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, helper.firstExpressionMissingKey());
}
// Invalid first expression
else if (!helper.isFirstExpressionValid(expression)) {
int startPosition = position(expression) + identifier.length() + 1 /* '(' */;
int endPosition = startPosition + helper.firstExpressionLength(expression);
addProblem(expression, startPosition, endPosition, helper.firstExpressionInvalidKey());
}
else {
expression.getFirstExpression().accept(this);
}
// Missing comma
if (helper.hasFirstExpression(expression) &&
!expression.hasComma()) {
int startPosition = position(expression) + identifier.length() + 1 /* '(' */;
if (helper.hasFirstExpression(expression)) {
startPosition += length(expression.getFirstExpression());
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, helper.missingCommaKey());
}
// Missing second expression
if (expression.hasComma()) {
if (!helper.hasSecondExpression(expression)) {
int startPosition = position(expression) + identifier.length() + 1 /* '(' */;
// First expression
if (helper.hasFirstExpression(expression)) {
startPosition += helper.firstExpressionLength(expression);
}
if (expression.hasComma()) {
startPosition++;
}
if (expression.hasSpaceAfterComma()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, helper.secondExpressionMissingKey());
}
// Invalid second expression
else if (!helper.isSecondExpressionValid(expression)) {
int startPosition = position(expression) + identifier.length() + 1 /* '(' */;
// First expression
if (helper.hasFirstExpression(expression)) {
startPosition += helper.firstExpressionLength(expression);
}
if (expression.hasComma()) {
startPosition++;
}
if (expression.hasSpaceAfterComma()) {
startPosition++;
}
int endPosition = startPosition + helper.secondExpressionLength(expression);
addProblem(expression, startPosition, endPosition, helper.secondExpressionInvalidKey());
}
else {
expression.getSecondExpression().accept(this);
}
}
}
}
private void validateAbstractFromClause(AbstractFromClause expression) {
if (expression.hasDeclaration()) {
// Two identification variable declarations have to be separated by a comma and
// the FROM clause cannot end with a comma
validateCollectionSeparatedByComma(
expression.getDeclaration(),
AbstractFromClause_IdentificationVariableDeclarationEndsWithComma,
AbstractFromClause_IdentificationVariableDeclarationIsMissingComma
);
// The identification variable declarations are evaluated from left to right in
// the FROM clause, and an identification variable declaration can use the result
// of a preceding identification variable declaration of the query string
List<Declaration> declarations = queryContext.getDeclarations();
for (int index = 0, count = declarations.size(); index < count; index++) {
Declaration declaration = declarations.get(index);
// Check the JOIN expressions in the identification variable declaration
if (declaration.isRange() && declaration.hasJoins()) {
List<Map.Entry<Join, String>> joinEntries = declaration.getJoinEntries();
for (int joinIndex = 0, joinCount = joinEntries.size(); joinIndex < joinCount; joinIndex++) {
Map.Entry<Join, String> joinEntry = joinEntries.get(joinIndex);
Join join = joinEntry.getKey();
// Retrieve the identification variable from the join association path
String variableName = queryContext.literal(
join.getJoinAssociationPath(),
LiteralType.PATH_EXPRESSION_IDENTIFICATION_VARIABLE
);
// Make sure the identification variable is defined before the JOIN expression
if (ExpressionTools.stringIsNotEmpty(variableName) &&
!isIdentificationVariableDeclaredBefore(variableName, index, joinIndex, declarations)) {
int startPosition = position(join.getJoinAssociationPath());
int endPosition = startPosition + variableName.length();
addProblem(
expression,
startPosition,
endPosition,
AbstractFromClause_WrongOrderOfIdentificationVariableDeclaration,
variableName
);
}
}
}
// Check the collection member declaration
else if (!declaration.isRange()) {
// Retrieve the identification variable from the path expression
String variableName = queryContext.literal(
declaration.getBaseExpression(),
LiteralType.PATH_EXPRESSION_IDENTIFICATION_VARIABLE
);
if (ExpressionTools.stringIsNotEmpty(variableName) &&
!isIdentificationVariableDeclaredBefore(variableName, index, -1, declarations)) {
int startPosition = position(declaration.getDeclarationExpression()) - variableName.length();
int endPosition = startPosition + variableName.length();
addProblem(
expression,
startPosition,
endPosition,
AbstractFromClause_WrongOrderOfIdentificationVariableDeclaration,
variableName
);
}
}
}
}
else {
int startPosition = position(expression) + FROM.length();
if (expression.hasSpaceAfterFrom()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(
expression,
startPosition,
endPosition,
AbstractFromClause_MissingIdentificationVariableDeclaration
);
}
}
private <T extends AbstractSingleEncapsulatedExpression>
void validateAbstractSingleEncapsulatedExpression
(T expression, AbstractSingleEncapsulatedExpressionHelper<T> helper) {
String identifier = helper.identifier(expression);
// Missing '('
if (!expression.hasLeftParenthesis()) {
int startPosition = position(expression) + identifier.length();
int endPosition = startPosition;
addProblem(
expression,
startPosition,
endPosition,
helper.leftParenthesisMissingKey(),
helper.arguments(expression)
);
}
// Missing encapsulated expression
else if (!helper.hasExpression(expression)) {
int startPosition = position(expression) +
identifier.length() +
1 /* '(' */ +
helper.lengthBeforeEncapsulatedExpression(expression);
int endPosition = startPosition;
addProblem(
expression,
startPosition,
endPosition,
helper.expressionMissingKey(),
helper.arguments(expression)
);
}
else {
if (!helper.isValidExpression(expression)) {
int startPosition = position(expression) +
identifier.length() +
1 /* '(' */ +
helper.lengthBeforeEncapsulatedExpression(expression);
int endPosition = startPosition + helper.encapsulatedExpressionLength(expression);
addProblem(
expression,
startPosition,
endPosition,
helper.expressionInvalidKey(),
helper.arguments(expression)
);
}
else {
super.visit(expression);
}
}
// Missing ')'
if (!expression.hasRightParenthesis()) {
int startPosition = position(expression) + length(expression);
int endPosition = startPosition;
addProblem(
expression,
startPosition,
endPosition,
helper.rightParenthesisMissingKey(),
helper.arguments(expression)
);
}
}
private <T extends AbstractTripleEncapsulatedExpression>
void validateAbstractTripleEncapsulatedExpression
(T expression, AbstractTripleEncapsulatedExpressionHelper<T> helper) {
String identifier = helper.identifier(expression);
// Missing '('
if (!expression.hasLeftParenthesis()) {
int startPosition = position(expression) + identifier.length();
int endPosition = startPosition;
addProblem(
expression,
startPosition,
endPosition,
helper.leftParenthesisMissingKey()
);
}
// Missing ')'
if (expression.hasLeftParenthesis() &&
helper.hasFirstExpression(expression) &&
!expression.hasRightParenthesis() &&
isRightParenthesisMissing(expression)) {
int startPosition = position(expression) + identifier.length() + 1 /* '(' */;
// First expression
if (helper.hasFirstExpression(expression)) {
startPosition += length(expression.getFirstExpression());
}
if (expression.hasFirstComma()) {
startPosition++;
}
if (expression.hasSpaceAfterFirstComma()) {
startPosition++;
}
// Second expression
if (helper.hasSecondExpression(expression)) {
startPosition += length(expression.getSecondExpression());
}
if (expression.hasSecondComma()) {
startPosition++;
}
if (expression.hasSpaceAfterSecondComma()) {
startPosition++;
}
// Third expression
if (helper.hasThirdExpression(expression)) {
startPosition += length(expression.getThirdExpression());
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, helper.rightParenthesisMissingKey());
}
if (expression.hasLeftParenthesis()) {
// Missing first expression
if (!helper.hasFirstExpression(expression)) {
int startPosition = position(expression) + identifier.length() + 1 /* '(' */;
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, helper.firstExpressionMissingKey());
}
// Invalid first expression
else if (!helper.isFirstExpressionValid(expression)) {
int startPosition = position(expression) + identifier.length() + 1 /* '(' */;
int endPosition = startPosition + helper.firstExpressionLength(expression);
addProblem(expression, startPosition, endPosition, helper.firstExpressionInvalidKey());
}
else {
expression.getFirstExpression().accept(this);
}
// Missing first comma
if (helper.hasFirstExpression(expression) &&
!expression.hasFirstComma()) {
int startPosition = position(expression) + identifier.length() + 1 /* '(' */;
if (helper.hasFirstExpression(expression)) {
startPosition += helper.firstExpressionLength(expression);
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, helper.firstCommaMissingKey());
}
// Validate second expression
if (expression.hasFirstComma()) {
int startPosition = position(expression) + identifier.length() + 1 /* '(' */;
if (helper.hasFirstExpression(expression)) {
startPosition += helper.firstExpressionLength(expression);
}
if (expression.hasFirstComma()) {
startPosition++;
}
if (expression.hasSpaceAfterFirstComma()) {
startPosition++;
}
// Missing second expression
if (!helper.hasSecondExpression(expression)) {
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, helper.secondExpressionMissingKey());
}
// Invalid second expression
else if (!helper.isSecondExpressionValid(expression)) {
int endPosition = startPosition + helper.secondExpressionLength(expression);
addProblem(expression, startPosition, endPosition, helper.secondExpressionInvalidKey());
}
else {
expression.getSecondExpression().accept(this);
}
}
// Missing second comma
if (helper.hasSecondExpression(expression) &&
!expression.hasSecondComma() &&
helper.hasThirdExpression(expression)) {
int startPosition = position(expression) + identifier.length() + 1 /* '(' */;
// First expression
if (helper.hasFirstExpression(expression)) {
startPosition += length(expression.getFirstExpression());
}
if (expression.hasFirstComma()) {
startPosition++;
}
if (expression.hasSpaceAfterFirstComma()) {
startPosition++;
}
// Second expression
if (helper.hasSecondExpression(expression)) {
startPosition += helper.secondExpressionLength(expression);
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, helper.secondCommaMissingKey());
}
// Validate third expression
if (expression.hasSecondComma()) {
int startPosition = position(expression) + identifier.length() + 1 /* '(' */;
// First expression
if (helper.hasFirstExpression(expression)) {
startPosition += helper.firstExpressionLength(expression);
}
if (expression.hasFirstComma()) {
startPosition++;
}
if (expression.hasSpaceAfterFirstComma()) {
startPosition++;
}
// Second expression
if (helper.hasSecondExpression(expression)) {
startPosition += helper.secondExpressionLength(expression);
}
if (expression.hasSecondComma()) {
startPosition++;
}
if (expression.hasSpaceAfterSecondComma()) {
startPosition++;
}
// Missing third expression
if (!helper.hasThirdExpression(expression)) {
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, helper.thirdExpressionMissingKey());
}
// Invalid third expression
else if (!helper.isThirdExpressionValid(expression)) {
int endPosition = startPosition + helper.thirdExpressionLength(expression);
addProblem(expression, startPosition, endPosition, helper.thirdExpressionInvalidKey());
}
else {
expression.getThirdExpression().accept(this);
}
}
}
}
private void validateArithmeticExpression(ArithmeticExpression expression) {
validateCompoundExpression(
expression,
expression.getArithmeticSign(),
ArithmeticExpression_MissingLeftExpression,
ArithmeticExpression_InvalidLeftExpression,
ArithmeticExpression_MissingRightExpression,
ArithmeticExpression_InvalidRightExpression,
ArithmeticExpressionBNF.ID,
ArithmeticTermBNF.ID
);
}
/**
* Validates
*
* @param expression
* @param endsWithCommadProblemKey The problem key describing the {@link CollectionExpression} is
* ending with a comma
* @param missingCommaProblemKey The problem key describing the {@link CollectionExpression} has
* two items not separated by a comma
*/
private void validateCollectionSeparatedByComma(Expression expression,
String endsWithCommaProblemKey,
String missingCommaProblemKey) {
CollectionSeparatedByCommaValidator validator = collectionSeparatedByCommaValidator();
try {
validator.endsWithCommaProblemKey = endsWithCommaProblemKey;
validator.wrongSeparatorProblemKey = missingCommaProblemKey;
expression.accept(validator);
}
finally {
validator.endsWithCommaProblemKey = null;
validator.wrongSeparatorProblemKey = null;
}
}
/**
* Validates
*
* @param expression
* @param endsWithCommadProblemKey The problem key describing the {@link CollectionExpression}
* is ending with a comma
* @param hasCommaProblemKey The problem key describing the {@link CollectionExpression} has two
* items separated by a comma
*/
private void validateCollectionSeparatedBySpace(Expression expression,
String endsWithCommaProblemKey,
String hasCommaProblemKey) {
CollectionSeparatedBySpaceValidator validator = collectionSeparatedBySpaceValidator();
try {
validator.endsWithCommaProblemKey = endsWithCommaProblemKey;
validator.wrongSeparatorProblemKey = hasCommaProblemKey;
expression.accept(validator);
}
finally {
validator.endsWithCommaProblemKey = null;
validator.wrongSeparatorProblemKey = null;
}
}
private void validateCompoundExpression(CompoundExpression expression,
String identifier,
String missingLeftExpression,
String invalidLeftExpression,
String missingRightExpression,
String invalidRightExpression,
String leftExpressionQueryBNF,
String rightExpressionQueryBNF) {
// Missing left expression
if (!expression.hasLeftExpression()) {
int startPosition = position(expression);
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, missingLeftExpression);
}
// Invalid left expression
else if (!isValid(expression.getLeftExpression(), leftExpressionQueryBNF)) {
int startPosition = position(expression);
int endPosition = startPosition + length(expression.getLeftExpression());
addProblem(expression, startPosition, endPosition, invalidLeftExpression);
}
else {
expression.getLeftExpression().accept(this);
}
// Missing right expression
if (!expression.hasRightExpression()) {
int startPosition = position(expression) + identifier.length();
if (expression.hasLeftExpression()) {
startPosition += length(expression.getLeftExpression()) + 1;
}
if (expression.hasSpaceAfterIdentifier()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, missingRightExpression);
}
// Invalid right expression
else if (!isValid(expression.getRightExpression(), rightExpressionQueryBNF)) {
int startPosition = position(expression) + identifier.length();
if (expression.hasLeftExpression()) {
startPosition += length(expression.getLeftExpression()) + 1;
}
if (expression.hasSpaceAfterIdentifier()) {
startPosition++;
}
int endPosition = startPosition + length(expression.getRightExpression());
addProblem(expression, startPosition, endPosition, invalidRightExpression);
}
else {
expression.getRightExpression().accept(this);
}
}
/**
* Validates the given variable name to make sure:
* <ul>
* <li>It is not a JPQL reserved identifier;</li>
* <li>It is a valid Java identifier.</li>
* </ul>
*
* @param expression The expression to validate
* @param variableName The text to actually validate
* @param variableLength The actual length of the text, which can be longer than the text that is
* validated
* @param reservedWordProblemKey The problem key used when the variable name is a reserved JPQL
* identifier
* @param invalidJavaIdentifierProblemKey The problem key used when the variable name is not a
* valid Java identifier
*/
private void validateIdentifier(Expression expression,
String variableName,
int variableLength,
String reservedWordProblemKey,
String invalidJavaIdentifierProblemKey) {
// Must not be a reserved identifier
if (AbstractExpression.isIdentifier(variableName)) {
int startPosition = position(expression);
int endPosition = startPosition + variableLength;
addProblem(expression, startPosition, endPosition, reservedWordProblemKey, variableName);
}
// The character sequence must begin with a Java identifier start character, and all other
// characters must be Java identifier part characters. An identifier start character is any
// character for which the method Character.isJavaIdentifierStart returns true. This includes
// the underscore (_) character and the dollar sign ($) character. An identifier part
// character is any character for which the method Character.isJavaIdentifierPart returns
// true. The question mark (?) character is reserved for use by the Java Persistence query
// language. An identification variable must not be a reserved identifier or have the same
// name as any entity in the same persistence unit
else if (!isValidJavaIdentifier(variableName)) {
int startPosition = position(expression);
int endPosition = startPosition + variableLength;
addProblem(expression, startPosition, endPosition, invalidJavaIdentifierProblemKey, variableName);
}
}
private void validateInputParameters(JPQLExpression expression) {
int positionalCount = 0;
int namedCount = 0;
for (InputParameter inputParameter : inputParameters) {
if (inputParameter.isNamed()) {
namedCount++;
}
else if (inputParameter.isPositional()) {
positionalCount++;
}
}
if ((positionalCount > 0) && (namedCount > 0)) {
for (InputParameter parameter : inputParameters) {
addProblem(parameter, InputParameter_Mixture);
}
}
}
private void validateLogicalExpression(LogicalExpression expression,
String leftExpressionQueryBNF,
String rightExpressionQueryBNF) {
validateCompoundExpression(
expression,
expression.getIdentifier(),
LogicalExpression_MissingLeftExpression,
LogicalExpression_InvalidLeftExpression,
LogicalExpression_MissingRightExpression,
LogicalExpression_InvalidRightExpression,
leftExpressionQueryBNF,
rightExpressionQueryBNF
);
}
private void validatePathExpression(AbstractPathExpression expression) {
// Missing identification variable
if (!expression.hasIdentificationVariable() &&
!expression.hasVirtualIdentificationVariable()) {
addProblem(expression, AbstractPathExpression_MissingIdentificationVariable);
}
// Cannot end with a dot
if (expression.endsWithDot()) {
addProblem(expression, AbstractPathExpression_CannotEndWithComma);
}
}
private void validateSelectStatement(AbstractSelectStatement expression) {
// Does not have a FROM clause
if (!expression.hasFromClause()) {
int startPosition = position(expression) + length(expression.getSelectClause());
if (expression.hasSpaceAfterSelect()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, AbstractSelectStatement_FromClauseMissing);
}
}
private AbstractSingleEncapsulatedExpressionHelper<ValueExpression> valueExpressionHelper() {
AbstractSingleEncapsulatedExpressionHelper<ValueExpression> helper = getHelper(VALUE);
if (helper == null) {
helper = buildValueExpressionHelper();
helpers.put(VALUE, helper);
}
return helper;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(AbsExpression expression) {
validateAbstractSingleEncapsulatedExpression(expression, absExpressionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(AbstractSchemaName expression) {
// Nothing to validate
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(AdditionExpression expression) {
validateArithmeticExpression(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(AllOrAnyExpression expression) {
validateAbstractSingleEncapsulatedExpression(expression, allOrAnyExpressionHelper());
// Make sure the expression is part of a comparison expression
if (!isChildOfComparisonExpession(expression)) {
addProblem(expression, AllOrAnyExpression_NotPartOfComparisonExpression);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(AndExpression expression) {
validateLogicalExpression(expression, ConditionalExpressionBNF.ID, ConditionalExpressionBNF.ID);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(ArithmeticFactor expression) {
// Missing expression after +/-
if (!expression.hasExpression()) {
int startPosition = position(expression) + 1;
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, ArithmeticFactor_MissingExpression);
}
else {
super.visit(expression);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(AvgFunction expression) {
validateAbstractSingleEncapsulatedExpression(expression, avgFunctionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(BadExpression expression) {
// Nothing to validate and we don't want
// to validate its encapsulated expression
}
/**
* {@inheritDoc}
*/
@Override
public void visit(BetweenExpression expression) {
// Missing expression before BETWEEN
if (!expression.hasExpression()) {
int startPosition = position(expression);
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, BetweenExpression_MissingExpression);
}
// Missing lower bound expression
if (!expression.hasLowerBoundExpression()) {
int startPosition = position(expression);
if (expression.hasExpression()) {
startPosition += length(expression.getExpression()) + 1;
}
startPosition += expression.hasNot() ? NOT_BETWEEN.length() : BETWEEN.length();
if (expression.hasSpaceAfterBetween()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, BetweenExpression_MissingLowerBoundExpression);
}
// Missing 'AND'
if (expression.hasLowerBoundExpression() &&
!expression.hasAnd()) {
int startPosition = position(expression);
if (expression.hasExpression()) {
startPosition += length(expression.getExpression()) + 1;
}
startPosition += expression.hasNot() ? NOT_BETWEEN.length() : BETWEEN.length();
if (expression.hasSpaceAfterBetween()) {
startPosition++;
}
startPosition += length(expression.getLowerBoundExpression());
if (expression.hasSpaceAfterLowerBound()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, BetweenExpression_MissingAnd);
}
// Missing upper bound expression
if (expression.hasAnd() &&
!expression.hasUpperBoundExpression()) {
int startPosition = position(expression);
if (expression.hasExpression()) {
startPosition += length(expression.getExpression()) + 1;
}
startPosition += expression.hasNot() ? NOT_BETWEEN.length() : BETWEEN.length();
if (expression.hasSpaceAfterBetween()) {
startPosition++;
}
if (expression.hasLowerBoundExpression()) {
startPosition += length(expression.getLowerBoundExpression());
}
if (expression.hasSpaceAfterLowerBound()) {
startPosition++;
}
startPosition += 3 /* 'AND' */;
if (expression.hasSpaceAfterAnd()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, BetweenExpression_MissingUpperBoundExpression);
}
// - Note that queries that contain subqueries on both sides of a
// comparison operation will not be portable across all databases.
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(CaseExpression expression) {
if (!isValidJPA20Identifier()) {
addProblem(expression, CaseExpression_InvalidJPAVersion);
}
else {
// When Clauses can't be separated by commas
if (expression.hasWhenClauses()) {
validateCollectionSeparatedBySpace(
expression.getWhenClauses(),
CaseExpression_WhenClausesEndWithComma,
CaseExpression_WhenClausesHasComma
);
}
// At least one WHEN clause must be specified
else {
int startPosition = position(expression) + CASE.length();
if (expression.hasSpaceAfterCase()) {
startPosition++;
}
if (expression.hasCaseOperand()) {
startPosition += length(expression.getCaseOperand());
}
if (expression.hasSpaceAfterCaseOperand()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, CaseExpression_MissingWhenClause);
}
// ELSE is missing
if (expression.hasWhenClauses() &&
!expression.hasElse()) {
int startPosition = position(expression) + CASE.length();
if (expression.hasSpaceAfterCase()) {
startPosition++;
}
if (expression.hasCaseOperand()) {
startPosition += length(expression.getCaseOperand());
}
if (expression.hasSpaceAfterCaseOperand()) {
startPosition++;
}
if (expression.hasWhenClauses()) {
startPosition += length(expression.getWhenClauses());
}
if (expression.hasSpaceAfterWhenClauses()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, CaseExpression_MissingElseIdentifier);
}
// ELSE expression is missing
if (expression.hasWhenClauses() &&
expression.hasElse() &&
!expression.hasElseExpression()) {
int startPosition = position(expression) + CASE.length();
if (expression.hasSpaceAfterCase()) {
startPosition++;
}
if (expression.hasCaseOperand()) {
startPosition += length(expression.getCaseOperand());
}
if (expression.hasSpaceAfterCaseOperand()) {
startPosition++;
}
if (expression.hasWhenClauses()) {
startPosition += length(expression.getWhenClauses());
}
if (expression.hasSpaceAfterWhenClauses()) {
startPosition++;
}
if (expression.hasElse()) {
startPosition += ELSE.length();
}
if (expression.hasSpaceAfterElse()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, CaseExpression_MissingElseExpression);
}
// END is missing
if (expression.hasWhenClauses() &&
expression.hasElseExpression() &&
!expression.hasEnd()) {
int startPosition = position(expression) + CASE.length();
if (expression.hasSpaceAfterCase()) {
startPosition++;
}
if (expression.hasCaseOperand()) {
startPosition += length(expression.getCaseOperand());
}
if (expression.hasSpaceAfterCaseOperand()) {
startPosition++;
}
if (expression.hasWhenClauses()) {
startPosition += length(expression.getWhenClauses());
}
if (expression.hasSpaceAfterWhenClauses()) {
startPosition++;
}
if (expression.hasElse()) {
startPosition += ELSE.length();
}
if (expression.hasSpaceAfterElse()) {
startPosition++;
}
startPosition += length(expression.getElseExpression());
if (expression.hasSpaceAfterElseExpression()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, CaseExpression_MissingEndIdentifier);
}
super.visit(expression);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(CoalesceExpression expression) {
if (isValidJPA20Identifier()) {
validateAbstractSingleEncapsulatedExpression(expression, coalesceExpressionHelper());
}
else {
addProblem(expression, CoalesceExpression_InvalidJPAVersion);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(CollectionExpression expression) {
// Nothing to validate, it's done by the parent expression
// but we want to validate its children
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(CollectionMemberDeclaration expression) {
// FROM => 'IN (x) AS y'
if (isOwnedByFromClause(expression)) {
// Missing left parenthesis
if (!expression.hasLeftParenthesis()) {
int startPosition = position(expression) + 2; // IN
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, CollectionMemberDeclaration_MissingLeftParenthesis);
}
// Missing collection valued path expression
else if (!expression.hasCollectionValuedPathExpression()) {
int startPosition = position(expression) + 3; // IN + '('
int endPosition = startPosition;
addProblem(
expression,
startPosition,
endPosition,
CollectionMemberDeclaration_MissingCollectionValuedPathExpression
);
}
// Missing right parenthesis
else if (!expression.hasRightParenthesis()) {
int startPosition = position(expression) + 2; // IN
if (expression.hasLeftParenthesis()) {
startPosition++;
}
startPosition += length(expression.getCollectionValuedPathExpression());
int endPosition = startPosition;
addProblem(
expression,
startPosition,
endPosition,
CollectionMemberDeclaration_MissingRightParenthesis
);
}
// Missing identification variable
if (expression.hasRightParenthesis() &&
!expression.hasIdentificationVariable()) {
int startPosition = position(expression) + 4; // IN + '(' + ')'
startPosition += length(expression.getCollectionValuedPathExpression());
if (expression.hasSpaceAfterRightParenthesis()) {
startPosition++;
}
if (expression.hasAs()) {
startPosition += 2;
}
if (expression.hasSpaceAfterAs()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(
expression,
startPosition,
endPosition,
CollectionMemberDeclaration_MissingIdentificationVariable
);
}
}
// Simple FROM => 'IN (x) AS y' or 'IN x'
else {
// TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(CollectionMemberExpression expression) {
// Missing entity expression
if (!expression.hasEntityExpression()) {
int startPosition = position(expression);
int endPosition = startPosition;
addProblem(
expression,
startPosition,
endPosition,
CollectionMemberExpression_MissingEntityExpression
);
}
// Missing collection valued path expression
if (!expression.hasCollectionValuedPathExpression()) {
int startPosition = position(expression);
if (expression.hasEntityExpression()) {
startPosition += length(expression.getEntityExpression()) + 1;
}
if (expression.hasNot()) {
startPosition += 4; // NOT = 3 + 1 space
}
startPosition += MEMBER.length();
if (expression.hasSpaceAfterMember()) {
startPosition++;
}
if (expression.hasOf()) {
startPosition += 2;
}
if (expression.hasSpaceAfterOf()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(
expression,
startPosition,
endPosition,
CollectionMemberExpression_MissingCollectionValuedPathExpression
);
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(CollectionValuedPathExpression expression) {
validatePathExpression(expression);
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(ComparisonExpression expression) {
// Missing left expression
if (!expression.hasLeftExpression()) {
int startPosition = position(expression);
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, ComparisonExpression_MissingLeftExpression);
}
// Missing right expression
if (!expression.hasRightExpression()) {
int startPosition = position(expression);
if (expression.hasLeftExpression()) {
startPosition += 1 + length(expression.getLeftExpression());
}
startPosition += expression.getComparisonOperator().length();
if (expression.hasSpaceAfterIdentifier()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, ComparisonExpression_MissingRightExpression);
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(ConcatExpression expression) {
validateAbstractSingleEncapsulatedExpression(expression, concatExpressionHelper());
if (expression.hasLeftParenthesis() &&
expression.hasExpression()) {
CollectionExpression collectionExpression = collectionExpression(expression.getExpression());
// Single element
if (collectionExpression == null) {
addProblem(expression, ConcatExpression_MissingExpression);
}
else {
for (Expression child : collectionExpression.getChildren()) {
if (!isValid(child, StringPrimaryBNF.ID)) {
addProblem(child, ConcatExpression_InvalidExpression, child.toParsedText());
}
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(ConstructorExpression expression) {
// Missing constructor name
if (expression.getClassName().length() == 0) {
int startPosition = position(expression) + 3;
if (expression.hasSpaceAfterNew()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, ConstructorExpression_MissingConstructorName);
}
// Missing left parenthesis
if (!expression.hasLeftParenthesis()) {
String className = expression.getClassName();
int startPosition = position(expression) + 3;
if (expression.hasSpaceAfterNew()) {
startPosition++;
}
if (className != null) {
startPosition += className.length();
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, ConstructorExpression_MissingLeftParenthesis);
}
// Missing constructor items
if (expression.hasLeftParenthesis()) {
if (!expression.hasConstructorItems()) {
String className = expression.getClassName();
int startPosition = position(expression) + 4 /* NEW + '(' */;
if (expression.hasSpaceAfterNew()) {
startPosition++;
}
if (className != null) {
startPosition += className.length();
}
startPosition += length(expression.getConstructorItems());
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, ConstructorExpression_MissingConstructorItem);
}
// Validate collection expression
else {
validateCollectionSeparatedByComma(
expression.getConstructorItems(),
ConstructorExpression_ConstructorItemEndsWithComma,
ConstructorExpression_ConstructorItemIsMissingComma
);
}
}
// Missing right parenthesis
if (expression.hasLeftParenthesis() &&
expression.hasConstructorItems() &&
!expression.hasRightParenthesis()) {
String className = expression.getClassName();
int startPosition = position(expression) + 4 /* NEW + '(' */;
if (expression.hasSpaceAfterNew()) {
startPosition++;
}
if (className != null) {
startPosition += className.length();
}
startPosition += length(expression.getConstructorItems());
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, ConstructorExpression_MissingRightParenthesis);
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(CountFunction expression) {
validateAbstractSingleEncapsulatedExpression(expression, countFunctionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(DateTime expression) {
String dateTime = expression.getText();
// The JDBC escape syntax
if (dateTime.startsWith("{")) {
int length = dateTime.length();
// Missing opening
if (!dateTime.startsWith("{d ") &&
!dateTime.startsWith("{t ") &&
!dateTime.startsWith("{ts ")) {
int startPosition = position(expression) + 1;
int endPosition = startPosition;
for (int index = 1; index < length; index++) {
if (Character.isWhitespace(dateTime.charAt(index))) {
break;
}
endPosition++;
}
addProblem(expression, startPosition, endPosition, DateTime_JDBCEscapeFormat_InvalidSpecification);
}
// Missing open quote
else if (!dateTime.startsWith("{d '") &&
!dateTime.startsWith("{t '") &&
!dateTime.startsWith("{ts '")) {
int startPosition = position(expression) + 1;
for (int index = 1; index < length; index++) {
startPosition++;
if (Character.isWhitespace(dateTime.charAt(index))) {
break;
}
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, DateTime_JDBCEscapeFormat_MissingOpenQuote);
}
// Missing closing '
if ((length > 1) && (dateTime.charAt(length - (dateTime.endsWith("}") ? 2 : 1)) != '\'')) {
int startPosition = position(expression) + length;
if (dateTime.endsWith("}")) {
startPosition--;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, DateTime_JDBCEscapeFormat_MissingCloseQuote);
}
// Missing closing }
else if (!dateTime.endsWith("}")) {
int startPosition = position(expression) +length;
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, DateTime_JDBCEscapeFormat_MissingRightCurlyBrace);
}
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(DeleteClause expression) {
// FROM is missing
if (!expression.hasFrom()) {
int startPosition = DELETE.length();
if (expression.hasSpaceAfterDelete()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, DeleteClause_FromMissing);
}
// No entity abstract schema type is declared
else if (!expression.hasRangeVariableDeclaration()) {
int startPosition = DELETE_FROM.length() + 1;
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, DeleteClause_RangeVariableDeclarationMissing);
}
// More than one entity abstract schema type is declared
CollectionExpression collectionExpression = collectionExpression(expression.getRangeVariableDeclaration());
if (collectionExpression != null) {
Expression firstChild = collectionExpression.getChild(0);
int startPosition = position(firstChild) + length(firstChild);
int endPosition = position(collectionExpression) + length(collectionExpression);
boolean malformed = false;
for (int index = collectionExpression.childrenSize() - 1; --index >= 0; ) {
if (!collectionExpression.hasComma(index)) {
malformed = true;
}
}
if (collectionExpression.toParsedText().endsWith(" ")) {
endPosition--;
}
addProblem(
expression,
startPosition,
endPosition,
malformed ? DeleteClause_RangeVariableDeclarationMalformed :
DeleteClause_MultipleRangeVariableDeclaration
);
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(DeleteStatement expression) {
// Nothing to validate, done directly by DeleteClause and WhereClause
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(DivisionExpression expression) {
validateArithmeticExpression(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(EmptyCollectionComparisonExpression expression) {
// Missing collection valued path expression
if (!expression.hasExpression()) {
int startPosition = position(expression);
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, EmptyCollectionComparisonExpression_MissingExpression);
}
else {
super.visit(expression);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(EntityTypeLiteral expression) {
// Nothing to validate
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(EntryExpression expression) {
if (isValidJPA20Identifier()) {
validateAbstractSingleEncapsulatedExpression(expression, entryExpressionHelper());
}
else {
addProblem(expression, EntryExpression_InvalidJPAVersion);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(ExistsExpression expression) {
validateAbstractSingleEncapsulatedExpression(expression, existsExpressionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(FromClause expression) {
validateAbstractFromClause(expression);
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(FuncExpression expression) {
if (isEclipseLinkPlatform()) {
validateAbstractSingleEncapsulatedExpression(expression, funcExpressionHelper());
// Missing SQL function name
if (expression.hasLeftParenthesis()) {
String functionName = expression.getFunctionName();
if (ExpressionTools.stringIsEmpty(functionName)) {
int startPosition = position(expression) + FUNC.length();
if (expression.hasLeftParenthesis()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, FuncExpression_MissingFunctionName);
}
}
super.visit(expression);
}
// Invalid platform
else {
addProblem(expression, FuncExpression_InvalidJPAPlatform);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(GroupByClause expression) {
// No group items are specified
if (!expression.hasGroupByItems()) {
int startPosition = position(expression.getGroupByItems());
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, GroupByClause_GroupByItemMissing);
}
// Validate the separation of multiple ordering items
else {
validateCollectionSeparatedByComma(
expression.getGroupByItems(),
GroupByClause_GroupByItemEndsWithComma,
GroupByClause_GroupByItemIsMissingComma
);
super.visit(expression);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(HavingClause expression) {
validateAbstractConditionalClause(
expression,
HavingClause_MissingConditionalExpression,
HavingClause_InvalidConditionalExpression
);
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(IdentificationVariable expression) {
if (!expression.isVirtual()) {
String variable = expression.getText();
validateIdentifier(
expression,
variable,
variable.length(),
IdentificationVariable_Invalid_ReservedWord,
IdentificationVariable_Invalid_JavaIdentifier
);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(IdentificationVariableDeclaration expression) {
// The range variable declaration is missing
if (!expression.hasRangeVariableDeclaration()) {
int startPosition = position(expression);
int endPosition = startPosition;
addProblem(
expression,
startPosition,
endPosition,
IdentificationVariableDeclaration_MissingRangeVariableDeclaration
);
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(IndexExpression expression) {
if (isValidJPA20Identifier()) {
validateAbstractSingleEncapsulatedExpression(expression, indexExpressionHelper());
}
else {
addProblem(expression, IndexExpression_InvalidJPAVersion);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(InExpression expression) {
// An expression must be specified
if (!expression.hasExpression()) {
int startPosition = position(expression);
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, InExpression_MissingExpression);
}
// Make sure it's a valid expression
else {
Expression pathExpression = expression.getExpression();
if (!isValid(pathExpression, StateFieldPathExpressionBNF.ID) &&
!isValid(pathExpression, TypeExpressionBNF.ID)) {
int startPosition = position(expression);
int endPosition = startPosition + length(expression.getExpression());
addProblem(expression, startPosition, endPosition, InExpression_MalformedExpression);
}
}
// Check for "IN :input_parameter" defined in JPA 2.0
boolean singleInputParameter = isValidJPA20Identifier() &&
expression.isSingleInputParameter() &&
!expression.hasRightParenthesis();
// Missing '('
if (!expression.hasLeftParenthesis() && !singleInputParameter) {
int startPosition = position(expression) + 2; // IN
if (expression.hasExpression()) {
startPosition += length(expression.getExpression()) + 1;
}
if (expression.hasNot()) {
startPosition += 4; // 3 (NOT) + 1 (whitespace)
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, InExpression_MissingLeftParenthesis);
}
// There must be at least one element in the comma separated list that
// defines the set of values for the IN expression.
else if (!expression.hasInItems()) {
int startPosition = position(expression) + 3; // (IN and '(')
if (expression.hasExpression()) {
startPosition += length(expression.getExpression()) + 1;
}
if (expression.hasNot()) {
startPosition += 4; // 3 (NOT) + 1 (whitespace)
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, InExpression_MissingInItems);
}
// Make sure the IN items are separated by commas
else {
// TODO: Validate each child
// !isValidWithChildBypass(expression.getInItems(), InItemBNF.ID)
validateCollectionSeparatedByComma(
expression.getInItems(),
InExpression_InItemEndsWithComma,
InExpression_InItemIsMissingComma
);
}
// Missing ')'
if (expression.hasLeftParenthesis() &&
expression.hasInItems() &&
!expression.hasRightParenthesis()) {
int startPosition = position(expression) + 3; // (IN and '(')
if (expression.hasExpression()) {
startPosition += length(expression.getExpression()) + 1;
}
if (expression.hasNot()) {
startPosition += 4; // 3 (NOT) + 1 (whitespace)
}
startPosition += length(expression.getInItems());
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, InExpression_MissingRightParenthesis);
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(InputParameter expression) {
inputParameters.add(expression);
String parameter = expression.getParameter();
// No parameter specified
if (parameter.length() == 1) {
int startPosition = position(expression);
int endPosition = startPosition + 1;
addProblem(expression, startPosition, endPosition, InputParameter_MissingParameter);
}
// Named parameter: It follows the rules for identifiers defined in Section 4.4.1 of the spec<
else if (expression.isNamed()) {
if (!isValidJavaIdentifier(parameter.substring(1))) {
int startPosition = position(expression);
int endPosition = startPosition + parameter.length();
addProblem(expression, startPosition, endPosition, InputParameter_JavaIdentifier);
}
}
// Positional parameter: Designated by the question mark (?) prefix followed by an integer
else {
boolean valid = true;
for (int index = parameter.length(); --index > 0; ) /* Skip ? */ {
char character = parameter.charAt(index);
if (!Character.isDigit(character)) {
int startPosition = position(expression);
int endPosition = startPosition + parameter.length();
addProblem(expression, startPosition, endPosition, InputParameter_NotInteger);
valid = false;
break;
}
}
// Input parameters are numbered starting from 1
if (valid) {
Integer value = Integer.valueOf(parameter.substring(1));
if (value < 1) {
int startPosition = position(expression);
int endPosition = startPosition + parameter.length();
addProblem(expression, startPosition, endPosition, InputParameter_SmallerThanOne);
}
}
}
// Input parameters can only be used in the WHERE or HAVING clause of a query.
// Skip the ORDER BY clause because it has its own validation rule. The exception
// to this rule is in a FUNC expression
OwningClauseVisitor visitor = owningClauseVisitor();
expression.accept(visitor);
try {
if ((visitor.whereClause == null) &&
(visitor.havingClause == null) &&
(visitor.orderByClause == null) &&
(visitor.updateClause == null) &&
(visitor.deleteClause == null) &&
!isInFuncExpressionInSelectClause(visitor, expression)) {
int startPosition = position(expression);
int endPosition = startPosition + parameter.length();
addProblem(expression, startPosition, endPosition, InputParameter_WrongClauseDeclaration);
}
}
finally {
visitor.dispose();
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(Join expression) {
// Missing join association path expression
if (!expression.hasJoinAssociationPath()) {
int startPosition = position(expression) + expression.getIdentifier().length();
if (expression.hasSpaceAfterJoin()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, Join_MissingJoinAssociationPath);
}
// Missing identification variable
if (expression.hasJoinAssociationPath() &&
!expression.hasIdentificationVariable()) {
int startPosition = position(expression) + expression.getIdentifier().length();
if (expression.hasSpaceAfterJoin()) {
startPosition++;
}
startPosition += length(expression.getJoinAssociationPath());
if (expression.hasSpaceAfterJoinAssociation()) {
startPosition++;
}
if (expression.hasAs()) {
startPosition += 2;
}
if (expression.hasSpaceAfterAs()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, Join_MissingIdentificationVariable);
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(JoinFetch expression) {
// Missing join association path expression
if (!expression.hasJoinAssociationPath()) {
int startPosition = position(expression) + expression.getIdentifier().toString().length();
if (expression.hasSpaceAfterFetch()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, JoinFetch_MissingJoinAssociationPath);
}
// The FETCH JOIN construct must not be used in the FROM clause of a subquery
if (isOwnedBySubFromClause(expression)) {
int startPosition = position(expression);
int endPosition = startPosition + length(expression);
addProblem(expression, startPosition, endPosition, JoinFetch_WrongClauseDeclaration);
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(JPQLExpression expression) {
// Invalid query: does not start with either SELECT, UPDATE or DELETE FROM
if (!expression.hasQueryStatement()) {
int startPosition = 0;
int endPosition = getQueryExpression().length();
addProblem(expression, startPosition, endPosition, JPQLExpression_InvalidQuery);
}
// Has an unknown ending statement
else if (expression.hasUnknownEndingStatement()) {
int startPosition = length(expression.getQueryStatement());
int endPosition = startPosition + length(expression.getUnknownEndingStatement());
addProblem(expression, startPosition, endPosition, JPQLExpression_UnknownEnding);
}
super.visit(expression);
// Now that the entire tree was visited, we can validate the input parameters, which were
// automatically cached. Positional and named parameters must not be mixed in a single query
validateInputParameters(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(KeyExpression expression) {
if (isValidJPA20Identifier()) {
validateAbstractSingleEncapsulatedExpression(expression, keyExpressionHelper());
}
else {
addProblem(expression, KeyExpression_InvalidJPAVersion);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(KeywordExpression expression) {
// Nothing to validate
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(LengthExpression expression) {
validateAbstractSingleEncapsulatedExpression(expression, lengthExpressionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(LikeExpression expression) {
// Missing string expression
if (!expression.hasStringExpression()) {
int startPosition = position(expression);
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, LikeExpression_MissingStringExpression);
}
// Missing pattern value
if (!expression.hasPatternValue()) {
int startPosition = position(expression) + length(expression.getStringExpression()) + 4; // 4 = LIKE
if (expression.hasSpaceAfterStringExpression()) {
startPosition++;
}
if (expression.hasNot()) {
startPosition += 4;
}
if (expression.hasSpaceAfterLike()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, LikeExpression_MissingPatternValue);
}
// Missing escape character
if (expression.hasEscape()) {
int startPosition = position(expression) + length(expression.getStringExpression()) + 4; // 4 = LIKE
if (expression.hasSpaceAfterStringExpression()) {
startPosition++;
}
if (expression.hasNot()) {
startPosition += 4;
}
if (expression.hasSpaceAfterLike()) {
startPosition++;
}
startPosition += length(expression.getPatternValue());
if (expression.hasSpaceAfterPatternValue()) {
startPosition++;
}
startPosition += ESCAPE.length();
if (expression.hasSpaceAfterEscape()) {
startPosition++;
}
// Validate the escape character
if (expression.hasEscapeCharacter()) {
Expression escapeCharacter = expression.getEscapeCharacter();
int endPosition = startPosition + length(escapeCharacter);
// Check for a string literal (single quoted character)
String character = queryContext.literal(escapeCharacter, LiteralType.STRING_LITERAL);
if (character.length() > 0) {
character = ExpressionTools.unquote(character);
if (character.length() != 1) {
addProblem(
expression,
startPosition,
endPosition,
LikeExpression_InvalidEscapeCharacter,
escapeCharacter.toParsedText()
);
}
}
else {
// Check for an input parameter
character = queryContext.literal(escapeCharacter, LiteralType.INPUT_PARAMETER);
if (character.length() == 0) {
addProblem(
expression,
startPosition,
endPosition,
LikeExpression_InvalidEscapeCharacter,
escapeCharacter.toParsedText()
);
}
}
}
else {
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, LikeExpression_MissingEscapeCharacter);
}
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(LocateExpression expression) {
validateAbstractTripleEncapsulatedExpression(expression, locateExpressionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(LowerExpression expression) {
validateAbstractSingleEncapsulatedExpression(expression, lowerExpressionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(MaxFunction expression) {
validateAbstractSingleEncapsulatedExpression(expression, maxFunctionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(MinFunction expression) {
validateAbstractSingleEncapsulatedExpression(expression, minFunctionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(ModExpression expression) {
validateAbstractDoubleEncapsulatedExpression(expression, modExpressionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(MultiplicationExpression expression) {
validateArithmeticExpression(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(NotExpression expression) {
// Missing expression
if (!expression.hasExpression()) {
int startPosition = position(expression) + 3 /* NOT */;
if (expression.hasSpaceAfterNot()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, NotExpression_MissingExpression);
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(NullComparisonExpression expression) {
// Missing expression
if (!expression.hasExpression()) {
int startPosition = position(expression);
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, NullComparisonExpression_MissingExpression);
}
else {
super.visit(expression);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(NullExpression expression) {
// Nothing to validate
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(NullIfExpression expression) {
if (isValidJPA20Identifier()) {
validateAbstractDoubleEncapsulatedExpression(expression, nullIfExpressionHelper());
}
else {
addProblem(expression, CoalesceExpression_InvalidJPAVersion);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(NumericLiteral expression) {
String text = expression.getText();
// • Exact numeric literals support the use of Java integer literal
// syntax as well as SQL exact numeric literal syntax
// • Approximate literals support the use Java floating point literal
// syntax as well as SQL approximate numeric literal syntax
// • Appropriate suffixes can be used to indicate the specific type
// of a numeric literal in accordance with the Java Language Specification
if (!isNumericLiteral(text)) {
int startPosition = position(expression);
int endPosition = startPosition + text.length();
addProblem(expression, startPosition, endPosition, NumericLiteral_Invalid, text);
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(ObjectExpression expression) {
validateAbstractSingleEncapsulatedExpression(expression, objectExpressionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(OrderByClause expression) {
if (!expression.hasOrderByItems()) {
int startPosition = position(expression.getOrderByItems());
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, OrderByClause_OrderByItemMissing);
}
// Validate the separation of multiple grouping items
else {
validateCollectionSeparatedByComma(
expression.getOrderByItems(),
OrderByClause_OrderByItemEndsWithComma,
OrderByClause_OrderByItemIsMissingComma
);
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(OrderByItem expression) {
// The Order By item is missing
if (!expression.hasExpression()) {
int startPosition = position(expression);
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, OrderByItem_MissingStateFieldPathExpression);
}
// EclipseLink: Make sure the expression is a scalar path expression or a result variable
// Java: Make sure the expression is a state field path expression or a result variable
else {
if (isEclipseLinkPlatform() && !isValid(expression.getExpression(), InternalOrderByItemBNF.ID)) {
int startPosition = position(expression);
int endPosition = startPosition + length(expression.getExpression());
addProblem(expression, startPosition, endPosition, OrderByItem_InvalidPath);
}
else if (isJavaPlatform()) {
ExpressionValidator validator = buildExpressionValidator(
StateFieldPathExpressionBNF.ID,
IdentificationVariableBNF.ID,
ResultVariableBNF.ID
);
expression.getExpression().accept(validator);
if (!validator.valid) {
int startPosition = position(expression);
int endPosition = startPosition + length(expression.getExpression());
addProblem(expression, startPosition, endPosition, OrderByItem_InvalidPath);
}
}
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(OrExpression expression) {
validateLogicalExpression(expression, ConditionalExpressionBNF.ID, ConditionalExpressionBNF.ID);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(RangeVariableDeclaration expression) {
// Missing abstract schema name
if (!expression.hasAbstractSchemaName()) {
int startPosition = position(expression);
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, RangeVariableDeclaration_MissingAbstractSchemaName);
}
// Missing identification variable
if (!expression.hasIdentificationVariable() &&
!expression.hasVirtualIdentificationVariable()) {
int startPosition = position(expression);
if (expression.hasAbstractSchemaName()) {
startPosition += length(expression.getAbstractSchemaName());
}
if (expression.hasSpaceAfterAbstractSchemaName()) {
startPosition++;
}
if (expression.hasAs()) {
startPosition += 2;
}
if (expression.hasSpaceAfterAs()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, RangeVariableDeclaration_MissingIdentificationVariable);
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(ResultVariable expression) {
if (isValidJPA20Identifier()) {
// Missing select expression
if (!expression.hasSelectExpression()) {
int startPosition = position(expression);
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, ResultVariable_MissingSelectExpression);
}
// Missing result variable
if (!expression.hasResultVariable()) {
int startPosition = position(expression) + 2 /* AS */;
if (expression.hasSelectExpression()) {
startPosition += length(expression.getSelectExpression()) + 1;
}
if (expression.hasSpaceAfterAs()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, ResultVariable_MissingResultVariable);
}
super.visit(expression);
}
else {
addProblem(expression, ResultVariable_InvalidJPAVersion);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SelectClause expression) {
// No select expression defined
if (!expression.hasSelectExpression()) {
int startPosition = position(expression) + length(expression);
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, AbstractSelectClause_SelectExpressionMissing);
}
else {
validateCollectionSeparatedByComma(
expression.getSelectExpression(),
AbstractSelectClause_SelectExpressionEndsWithComma,
AbstractSelectClause_SelectExpressionIsMissingComma
);
super.visit(expression);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SelectStatement expression) {
validateSelectStatement(expression);
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SimpleFromClause expression) {
validateAbstractFromClause(expression);
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SimpleSelectClause expression) {
// Select expression cannot be a collection
if (expression.hasSelectExpression()) {
Expression selectExpression = expression.getSelectExpression();
if (collectionExpression(selectExpression) != null) {
addProblem(
selectExpression,
SimpleSelectClause_NotSingleExpression,
selectExpression.toParsedText()
);
}
super.visit(expression);
}
// No select expression defined
else {
int startPosition = position(expression) + length(expression);
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, AbstractSelectClause_SelectExpressionMissing);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SimpleSelectStatement expression) {
if (isOwnedByConditionalClause(expression)) {
validateSelectStatement(expression);
queryContext.newSubqueryContext(expression);
try {
super.visit(expression);
}
finally {
queryContext.disposeSubqueryContext();
}
}
// Subqueries may be used in the WHERE or HAVING clause
else {
addProblem(expression, SimpleSelectStatement_InvalidLocation);
}
// - Note that some contexts in which a subquery can be used require that
// the subquery be a scalar subquery (i.e., produce a single result).
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SizeExpression expression) {
validateAbstractSingleEncapsulatedExpression(expression, sizeExpressionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SqrtExpression expression) {
validateAbstractSingleEncapsulatedExpression(expression, sqrtExpressionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(StateFieldPathExpression expression) {
validatePathExpression(expression);
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(StringLiteral expression) {
if (!expression.hasCloseQuote()) {
addProblem(expression, StringLiteral_MissingClosingQuote);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SubExpression expression) {
// Missing sub-expression
if (!expression.hasExpression()) {
int startPosition = position(expression);
int endPosition = startPosition + 1;
if (expression.hasRightParenthesis()) {
endPosition++;
}
addProblem(expression, startPosition, endPosition, SubExpression_MissingExpression);
}
// Missing right parenthesis
if (!expression.hasRightParenthesis()) {
int startPosition = position(expression);
if (expression.hasExpression()) {
startPosition += length(expression.getExpression()) + 1;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, SubExpression_MissingRightParenthesis);
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SubstringExpression expression) {
validateAbstractTripleEncapsulatedExpression(expression, substringExpressionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SubtractionExpression expression) {
validateArithmeticExpression(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SumFunction expression) {
validateAbstractSingleEncapsulatedExpression(expression, sumFunctionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(TreatExpression expression) {
// TODO: Validate syntax
if (isEclipseLinkPlatform()) {
super.visit(expression);
}
// Invalid platform
else {
addProblem(expression, FuncExpression_InvalidJPAPlatform);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(TrimExpression expression) {
validateAbstractSingleEncapsulatedExpression(expression, trimExpressionHelper());
// Missing string primary
if (!expression.hasExpression()) {
int startPosition = position(expression) + 4 /* TRIM */;
if (expression.hasLeftParenthesis()) {
startPosition++;
}
if (expression.hasSpecification()) {
startPosition += expression.getSpecification().name().length();
}
if (expression.hasSpaceAfterSpecification()) {
startPosition++;
}
if (expression.hasTrimCharacter()) {
startPosition += length(expression.getTrimCharacter());
}
if (expression.hasSpaceAfterTrimCharacter()) {
startPosition++;
}
if (expression.hasFrom()) {
startPosition += 4;
}
if (expression.hasSpaceAfterFrom()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, TrimExpression_MissingExpression);
}
// Invalid string primary
else if (!isValid(expression.getExpression(), StringPrimaryBNF.ID)) {
int startPosition = position(expression) + 4 /* TRIM */;
if (expression.hasLeftParenthesis()) {
startPosition++;
}
if (expression.hasSpecification()) {
startPosition += expression.getSpecification().name().length();
}
if (expression.hasSpaceAfterSpecification()) {
startPosition++;
}
if (expression.hasTrimCharacter()) {
startPosition += length(expression.getTrimCharacter());
}
if (expression.hasSpaceAfterTrimCharacter()) {
startPosition++;
}
if (expression.hasFrom()) {
startPosition += 4;
}
if (expression.hasSpaceAfterFrom()) {
startPosition++;
}
int endPosition = startPosition + length(expression.getExpression());
addProblem(expression, startPosition, endPosition, TrimExpression_InvalidExpression);
}
// Invalid trim character
if (expression.hasTrimCharacter()) {
Expression trimCharacter = expression.getTrimCharacter();
// Make sure it's not an input parameter
String inputParameter = queryContext.literal(trimCharacter, LiteralType.INPUT_PARAMETER);
if (ExpressionTools.stringIsEmpty(inputParameter)) {
String stringLiteral = queryContext.literal(trimCharacter, LiteralType.STRING_LITERAL);
int startPosition = position(expression) + 4 /* TRIM */;
if (expression.hasLeftParenthesis()) {
startPosition++;
}
if (expression.hasSpecification()) {
startPosition += expression.getSpecification().name().length();
}
if (expression.hasSpaceAfterSpecification()) {
startPosition++;
}
int endPosition = startPosition + length(expression.getTrimCharacter());
if (ExpressionTools.stringIsEmpty(stringLiteral)) {
addProblem(trimCharacter, startPosition, endPosition, TrimExpression_InvalidTrimCharacter);
}
else {
stringLiteral = stringLiteral.substring(1, stringLiteral.length() - (stringLiteral.endsWith("'") ? 1 : 0));
if (stringLiteral.length() != 1) {
addProblem(trimCharacter, startPosition, endPosition, TrimExpression_NotSingleStringLiteral);
}
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(TypeExpression expression) {
if (isValidJPA20Identifier()) {
validateAbstractSingleEncapsulatedExpression(expression, typeExpressionHelper());
}
else {
addProblem(expression, TypeExpression_InvalidJPAVersion);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(UnknownExpression expression) {
// Nothing to validate and we don't want
// to validate its encapsulated expression
}
/**
* {@inheritDoc}
*/
@Override
public void visit(UpdateClause expression) {
// Missing range variable declaration
if (!expression.hasRangeVariableDeclaration()) {
int startPosition = position(expression) + UPDATE.length();
if (expression.hasSpaceAfterUpdate()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, UpdateClause_MissingRangeVariableDeclaration);
}
// Missing 'SET'
else if (!expression.hasSet()) {
int startPosition = position(expression) + UPDATE.length();
if (expression.hasSpaceAfterUpdate()) {
startPosition++;
}
if (expression.hasRangeVariableDeclaration()) {
startPosition += length(expression.getRangeVariableDeclaration());
}
if (expression.hasSpaceAfterRangeVariableDeclaration()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, UpdateClause_MissingSet);
}
// Missing update items
else if (!expression.hasUpdateItems()) {
int startPosition = position(expression) + UPDATE.length();
if (expression.hasSpaceAfterUpdate()) {
startPosition++;
}
if (expression.hasRangeVariableDeclaration()) {
startPosition += length(expression.getRangeVariableDeclaration());
}
if (expression.hasSpaceAfterRangeVariableDeclaration()) {
startPosition++;
}
startPosition += 3; // SET
if (expression.hasSpaceAfterSet()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, UpdateClause_MissingUpdateItems);
}
// Make sure the update items are separated by commas
else {
validateCollectionSeparatedByComma(
expression.getUpdateItems(),
UpdateClause_UpdateItemEndsWithComma,
UpdateClause_UpdateItemIsMissingComma
);
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(UpdateItem expression) {
// Missing state field path expression
if (!expression.hasStateFieldPathExpression()) {
int startPosition = position(expression);
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, UpdateItem_MissingStateFieldPathExpression);
}
// Missing '='
if (expression.hasStateFieldPathExpression() &&
!expression.hasEqualSign()) {
int startPosition = position(expression) + length(expression.getStateFieldPathExpression());
if (expression.hasSpaceAfterStateFieldPathExpression()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, UpdateItem_MissingEqualSign);
}
// Missing new value
if (expression.hasEqualSign()) {
if (!expression.hasNewValue()) {
int startPosition = position(expression) + 1 /* '=' */;
if (expression.hasStateFieldPathExpression()) {
startPosition += length(expression.getStateFieldPathExpression());
}
if (expression.hasSpaceAfterStateFieldPathExpression()) {
startPosition++;
}
if (expression.hasSpaceAfterEqualSign()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, UpdateItem_MissingNewValue);
}
// Invalid new value
else {
// TODO: I have no example of something that can be parsed but that is invalid
// ExpressionValidator validator = newValueBNFValidator();
// expression.getNewValue().accept(validator);
//
// if (!validator.valid) {
// int startPosition = position(expression) + 1 /* '=' */;
// startPosition += UPDATE.length();
// startPosition += length(expression.getStateFieldPathExpression());
//
// if (expression.hasSpaceAfterStateFieldPathExpression())
// {
// startPosition++;
// }
//
// if (expression.hasSpaceAfterEqualSign())
// {
// startPosition++;
// }
//
// int endPosition = startPosition + length(expression.getNewValue());
//
// addProblem
// (
// expression,
// startPosition,
// endPosition,
// UpdateItem_InvalidNewValue
// );
// }
}
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(UpdateStatement expression) {
// Done directly by UpdateClause and WhereClause
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(UpperExpression expression) {
validateAbstractSingleEncapsulatedExpression(expression, upperExpressionHelper());
}
/**
* {@inheritDoc}
*/
@Override
public void visit(ValueExpression expression) {
if (isValidJPA20Identifier()) {
validateAbstractSingleEncapsulatedExpression(expression, valueExpressionHelper());
}
else {
addProblem(expression, ValueExpression_InvalidJPAVersion);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(WhenClause expression) {
// WHEN expression is missing
if (!expression.hasWhenExpression()) {
int startPosition = position(expression) + THEN.length();
if (expression.hasSpaceAfterWhen()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, WhenClause_MissingWhenExpression);
}
// THEN identifier is missing
if (expression.hasWhenExpression() &&
!expression.hasThen()) {
int startPosition = position(expression) + THEN.length();
if (expression.hasSpaceAfterWhen()) {
startPosition++;
}
startPosition += length(expression.getWhenExpression());
if (expression.hasSpaceAfterWhenExpression()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, WhenClause_MissingThenIdentifier);
}
// THEN expression is missing
if (expression.hasThen() &&
!expression.hasThenExpression()) {
int startPosition = position(expression) + THEN.length();
if (expression.hasSpaceAfterWhen()) {
startPosition++;
}
startPosition += length(expression.getWhenExpression());
if (expression.hasSpaceAfterWhenExpression()) {
startPosition++;
}
startPosition += THEN.length();
if (expression.hasSpaceAfterThen()) {
startPosition++;
}
int endPosition = startPosition;
addProblem(expression, startPosition, endPosition, WhenClause_MissingThenExpression);
}
super.visit(expression);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(WhereClause expression) {
validateAbstractConditionalClause(
expression,
WhereClause_MissingConditionalExpression,
WhereClause_InvalidConditionalExpression
);
super.visit(expression);
}
/**
* This validate is responsible to validate the collection of {@link Expression Expressions}:
* <ul>
* <li>Making sure they are all separated by a comma or by a space (depending on which one is
* required);</li>
* <li>Making sure it does not end with a comma;</li>
* <li>There is no empty expression between two commas.</li>
* </ul>
*/
private abstract class AbstractCollectionValidator extends AbstractExpressionVisitor {
String endsWithCommaProblemKey;
boolean validateOnly;
String wrongSeparatorProblemKey;
private void validateEndsWithComma(CollectionExpression expression) {
if (expression.endsWithComma()) {
int lastIndex = expression.childrenSize() - 1;
int length = expression.toParsedText(lastIndex).length();
int startPosition = position(expression) + length - 1;
if (expression.endsWithSpace()) {
startPosition--;
}
int endPosition = startPosition + 1;
if (!validateOnly) {
addProblem(expression, startPosition, endPosition, endsWithCommaProblemKey);
}
}
}
private void validateSeparation(CollectionExpression expression) {
for (int index = 0, count = expression.childrenSize(); index + 1 < count; index++) {
Expression expression1 = expression.getChild(index);
if (isNull(expression1)) {
int startPosition = position(expression1);
int endPosition = startPosition;
addProblem(
expression,
startPosition,
endPosition,
CollectionExpression_MissingExpression,
String.valueOf(index + 1)
);
}
if (!validateSeparator(expression, index)) {
Expression expression2 = expression.getChild(index + 1);
int startPosition = position(expression1) + length(expression1);
int endPosition = position(expression2);
// The space is part of the child expression, move backward
if (!expression.hasSpace(index)) {
startPosition--;
}
if (!validateOnly) {
addProblem(
expression,
startPosition,
endPosition,
wrongSeparatorProblemKey,
expression1.toParsedText(),
expression2.toParsedText()
);
}
}
}
}
/**
* Validates
*
* @param expression
* @param index
* @return
*/
abstract boolean validateSeparator(CollectionExpression expression, int index);
/**
* {@inheritDoc}
*/
@Override
public void visit(CollectionExpression expression) {
validateSeparation(expression);
validateEndsWithComma(expression);
}
}
private abstract class AbstractDoubleEncapsulatedExpressionHelper<T extends AbstractDoubleEncapsulatedExpression>
implements AbstractEncapsulatedExpressionHelper<T> {
/**
* {@inheritDoc}
*/
public String[] arguments(T expression) {
return ExpressionTools.EMPTY_STRING_ARRAY;
}
abstract String firstExpressionInvalidKey();
/**
* {@inheritDoc}
*/
public int firstExpressionLength(T expression) {
return length(expression.getFirstExpression());
}
abstract String firstExpressionMissingKey();
/**
* {@inheritDoc}
*/
public boolean hasFirstExpression(T expression) {
return expression.hasFirstExpression();
}
/**
* {@inheritDoc}
*/
public boolean hasSecondExpression(T expression) {
return expression.hasSecondExpression();
}
abstract boolean isFirstExpressionValid(T expression);
abstract boolean isSecondExpressionValid(T expression);
abstract String missingCommaKey();
abstract String secondExpressionInvalidKey();
/**
* {@inheritDoc}
*/
public int secondExpressionLength(T expression) {
return length(expression.getSecondExpression());
}
abstract String secondExpressionMissingKey();
}
/**
* The root helper that validates any {@link AbstractEncapsulatedExpression}.
*
* @see AbstractDoubleEncapsulatedExpressionHelper
* @see AbstractSingleEncapsulatedExpressionHelper
* @see AbstractTripleEncapsulatedExpressionHelper
*/
private interface AbstractEncapsulatedExpressionHelper<T extends AbstractEncapsulatedExpression> {
/**
* Returns the arguments that can help to format the localized problem.
*
* @param expression The {@link AbstractEncapsulatedExpression} being validated
* @return The list of arguments used to complete the localized problem
*/
String[] arguments(T expression);
/**
* Returns the JPQL identifier of the given {@link AbstractEncapsulatedExpression}.
*
* @param expression The {@link AbstractEncapsulatedExpression} being validated
* @return The JPQL identifier of the given {@link AbstractEncapsulatedExpression}
*/
String identifier(T expression);
/**
* Returns the message key for the problem describing that the left parenthesis is missing.
*
* @return The key used to retrieve the localized message
*/
String leftParenthesisMissingKey();
/**
* Returns the message key for the problem describing that the right parenthesis is missing.
*
* @return The key used to retrieve the localized message
*/
String rightParenthesisMissingKey();
}
/**
* The abstract implementation of {@link AbstractSingleEncapsulatedExpressionHelper} which
* implements some of the methods since the behavior is the same for all subclasses of {@link
* AbstractSingleEncapsulatedExpression}.
*/
private abstract class AbstractSingleEncapsulatedExpressionHelper<T extends AbstractSingleEncapsulatedExpression>
implements AbstractEncapsulatedExpressionHelper<T> {
/**
* {@inheritDoc}
*/
public String[] arguments(T expression) {
return ExpressionTools.EMPTY_STRING_ARRAY;
}
/**
* {@inheritDoc}
*/
public int encapsulatedExpressionLength(T expression) {
return length(expression.getExpression());
}
/**
* Returns
*
* @return
*/
abstract String expressionInvalidKey();
/**
* Returns
*
* @return
*/
abstract String expressionMissingKey();
/**
* {@inheritDoc}
*/
public boolean hasExpression(T expression) {
return expression.hasExpression();
}
/**
* {@inheritDoc}
*/
public final String identifier(T expression) {
return expression.getIdentifier();
}
/**
* {@inheritDoc}
*/
public boolean isValidExpression(T expression) {
return isValid(expression.getExpression(), expression.encapsulatedExpressionBNF());
}
/**
* {@inheritDoc}
*/
public int lengthBeforeEncapsulatedExpression(T expression) {
// By default, there is no text after the left parenthesis and the encapsulated expression
// but there are exception, such as the functions (AVG, COUNT, MIN, MAX, SUM)
return 0;
}
}
private abstract class AbstractTripleEncapsulatedExpressionHelper<T extends AbstractTripleEncapsulatedExpression>
implements AbstractEncapsulatedExpressionHelper<T> {
/**
* {@inheritDoc}
*/
public String[] arguments(T expression) {
return ExpressionTools.EMPTY_STRING_ARRAY;
}
abstract String firstCommaMissingKey();
abstract String firstExpressionInvalidKey();
/**
* {@inheritDoc}
*/
public int firstExpressionLength(T expression) {
return length(expression.getFirstExpression());
}
abstract String firstExpressionMissingKey();
/**
* {@inheritDoc}
*/
public boolean hasFirstExpression(T expression) {
return expression.hasFirstExpression();
}
/**
* {@inheritDoc}
*/
public boolean hasSecondExpression(T expression) {
return expression.hasSecondExpression();
}
/**
* {@inheritDoc}
*/
public boolean hasThirdExpression(T expression) {
return expression.hasThirdExpression();
}
abstract boolean isFirstExpressionValid(T expression);
abstract boolean isSecondExpressionValid(T expression);
abstract boolean isThirdExpressionValid(T expression);
abstract String secondCommaMissingKey();
abstract String secondExpressionInvalidKey();
/**
* {@inheritDoc}
*/
public int secondExpressionLength(T expression) {
return length(expression.getSecondExpression());
}
abstract String secondExpressionMissingKey();
abstract String thirdExpressionInvalidKey();
/**
* {@inheritDoc}
*/
public int thirdExpressionLength(T expression) {
return length(expression.getThirdExpression());
}
abstract String thirdExpressionMissingKey();
}
/**
* This validator validates a {@link CollectionExpression} by making sure each item is separated
* by a comma.
*/
private class CollectionSeparatedByCommaValidator extends AbstractCollectionValidator {
/**
* {@inheritDoc}
*/
@Override
boolean validateSeparator(CollectionExpression expression, int index) {
return expression.hasComma(index);
}
}
/**
* This validator validates a {@link CollectionExpression} by making sure each item is not
* separated by a comma.
*/
private class CollectionSeparatedBySpaceValidator extends AbstractCollectionValidator {
/**
* {@inheritDoc}
*/
@Override
boolean validateSeparator(CollectionExpression expression, int index) {
return !expression.hasComma(index);
}
}
private static class ComparisonExpressionVisitor extends AbstractExpressionVisitor {
/**
* The {@link ComparisonExpression} if it is the {@link Expression} that was visited.
*/
ComparisonExpression expression;
/**
* {@inheritDoc}
*/
@Override
public void visit(ComparisonExpression expression) {
this.expression = expression;
}
}
/**
* This visitor is responsible to traverse the parents of the visited {@link Expression} and
* stops if a parent is {@link FuncExpression}.
*/
private static class FuncExpressionFinder extends AbstractTraverseParentVisitor {
/**
* The {@link FunctionExpression} if it is a parent of the {@link Expression} being visited.
*/
private FuncExpression expression;
/**
* {@inheritDoc}
*/
@Override
public void visit(FuncExpression expression) {
this.expression = expression;
}
}
}