
org.eclipse.persistence.jpa.internal.jpql.AbstractJPQLQueryHelper Maven / Gradle / Ivy
Show all versions of eclipselink Show documentation
/*******************************************************************************
* 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 protected License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse protected 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.Comparator;
import java.util.List;
import java.util.TreeSet;
import org.eclipse.persistence.jpa.internal.jpql.parser.Expression;
import org.eclipse.persistence.jpa.internal.jpql.parser.InputParameter;
import org.eclipse.persistence.jpa.internal.jpql.parser.JPQLExpression;
import org.eclipse.persistence.jpa.internal.jpql.parser.QueryPosition;
import org.eclipse.persistence.jpa.jpql.ContentAssistProposals;
import org.eclipse.persistence.jpa.jpql.JPQLQueryProblem;
import org.eclipse.persistence.jpa.jpql.TypeHelper;
import org.eclipse.persistence.jpa.jpql.spi.IManagedTypeProvider;
import org.eclipse.persistence.jpa.jpql.spi.IQuery;
import org.eclipse.persistence.jpa.jpql.spi.IType;
import org.eclipse.persistence.jpa.jpql.spi.ITypeRepository;
/**
* This helper can perform the following operations over a JPQL query:
*
* - Calculates the result type of a query: {@link #getResultType()};
* - Calculates the type of an input parameter: {@link #getParameterType(String)}.
* - Calculates the possible choices to complete the query from a given
* position (used for content assist): {@link #buildContentAssistItems(int)}.
* - Validates the query by introspecting it grammatically and semantically:
*
* - {@link #validate()},
* - {@link #validateGrammar()},
* - {@link #validateSemantic()}.
*
*
* @version 2.3
* @since 2.3
* @author Pascal Filion
*/
public abstract class AbstractJPQLQueryHelper {
/**
* This visitor is responsible to gather the possible proposals based on the position of the
* caret within the JPQL query.
*/
private ContentAssistVisitor contentAssistVisitor;
/**
* This visitor is responsible to visit the entire parsed tree representation of the JPQL query
* and to validate its content based on the JPQL grammar.
*/
private GrammarValidator grammarValidator;
/**
* The context used to query information about the JPQL query.
*/
private final JPQLQueryContext queryContext;
/**
* This visitor is responsible to visit the entire parsed tree representation of the JPQL query
* and to validate the semantic of the information.
*/
private SemanticValidator semanticValidator;
/**
* Creates a new AbstractJPQLQueryHelper
.
*/
protected AbstractJPQLQueryHelper() {
this(new JPQLQueryContext());
}
/**
* Creates a new AbstractJPQLQueryHelper
.
*
* @param queryContext The context used to query information about the JPQL query
*/
protected AbstractJPQLQueryHelper(JPQLQueryContext queryContext) {
super();
this.queryContext = queryContext;
}
/**
* Retrieves the possibles choices that can complete the query from the given position within
* the query.
*
* Note: Disposing of the internal data is not done automatically.
*
* @param position The position within the query for which a list of possible choices are created
* for completing the query
* @return The list of valid proposals regrouped by categories
*/
protected ContentAssistProposals buildContentAssistProposals(int position) {
// Create a map of the positions within the parsed tree
QueryPosition queryPosition = getJPQLExpression().buildPosition(
queryContext.getQuery().getExpression(),
position
);
// Visit the expression, which will collect the possible proposals
ContentAssistVisitor visitor = contentAssistVisitor();
try {
visitor.prepare(queryPosition);
queryPosition.getExpression().accept(visitor);
return visitor.getProposals();
}
finally {
visitor.dispose();
}
}
private ContentAssistVisitor contentAssistVisitor() {
if (contentAssistVisitor == null) {
contentAssistVisitor = new ContentAssistVisitor(queryContext);
}
return contentAssistVisitor;
}
/**
* Disposes of the internal data.
*/
protected void dispose() {
queryContext.dispose();
}
private JPQLExpression getJPQLExpression() {
return queryContext.getJPQLExpression();
}
/**
* Retrieves, if it can be determined, the type of the given input parameter with the given name.
* The type will be guessed based on its location within expression.
*
* Note: Both named and positional input parameter can be used.
*
* @param parameterName The name of the input parameter to retrieve its type, which needs to be
* prepended by ':' or '?'
* @return Either the closest type of the input parameter or null
if the type
* couldn't be determined
*/
protected IType getParameterType(String parameterName) {
// Retrieve the input parameter's qualifier (':' or '?')
char character = parameterName.length() > 0 ? parameterName.charAt(0) : '\0';
// Does not begin with either ':' or '?'
if ((character != ':') && (character != '?')) {
return getTypeHelper().objectType();
}
// Find the InputParameters with the given parameter name
Collection inputParameters = queryContext.findInputParameters(parameterName);
// No InputParameter was found
if (inputParameters.isEmpty()) {
return getTypeHelper().objectType();
}
// Now find the closest type for each location
TreeSet types = new TreeSet(buildNumericTypeComparator());
for (InputParameter inputParameter : inputParameters) {
IType type = queryContext.getParameterType(inputParameter);
// A type is ignored if it cannot be determined and it can't affect the calculation
// if the same input parameter is used elsewhere. Example:
// SELECT e FROM Employee e WHERE :name IS NOT NULL AND e.name = 'JPQL'
// The first :name cannot be used to calculate the type
if (type.isResolvable()) {
types.add(type);
}
}
return types.isEmpty() ? getTypeHelper().objectType() : types.first();
}
private Comparator buildNumericTypeComparator() {
return new NumericTypeComparator(getTypeHelper());
}
/**
* Returns the string representation of the parsed tree.
*
* @return The string created from the parsed tree representation of the original JPQL query
*/
protected String getParsedJPQLQuery() {
return getJPQLExpression().toParsedText();
}
/**
* Returns the provider for managed types (entities, embeddables, mapped superclasses).
*
* @return The container of managed types
*/
protected IManagedTypeProvider getProvider() {
return getQuery().getProvider();
}
/**
* Returns the external form representing a named query.
*
* @return The external form representing a named query
*/
protected IQuery getQuery() {
return queryContext.getQuery();
}
/**
* Returns the {@link JPQLQueryContext} that contains information about the JPQL query.
*
* @return The {@link JPQLQueryContext} that contains information contained in the JPQL query
*/
protected JPQLQueryContext getQueryContext() {
return queryContext;
}
/**
* Calculates the type of the query result of the JPQL query.
*
* See {@link TypeVisitor} to understand how the type is calculated.
*
* @return The result type of the JPQL query if it could accurately be calculated or the
* {@link IClass} for Object
if it could not be calculated
*/
protected IType getResultType() {
IType type = queryContext.getType(getJPQLExpression());
if (!type.isResolvable()) {
type = getTypeHelper().objectType();
}
return type;
}
/**
* Returns the {@link IType} representing the given Java type.
*
* @param type The Java type for which its external form is requested
* @return The external form for the given Java type
*/
protected IType getType(Class> type) {
return getTypeRepository().getType(type);
}
/**
* Returns a helper that gives access to the most common {@link IType types}.
*
* @return A helper containing a collection of methods related to {@link IType}
*/
protected TypeHelper getTypeHelper() {
return getTypeRepository().getTypeHelper();
}
/**
* Returns the repository that gives access to the application's types.
*
* @return The repository for classes, interfaces, enum types and annotations
*/
protected ITypeRepository getTypeRepository() {
return getProvider().getTypeRepository();
}
private GrammarValidator grammarValidator() {
if (grammarValidator == null) {
grammarValidator = new GrammarValidator(queryContext);
}
return grammarValidator;
}
private SemanticValidator semanticValidator() {
if (semanticValidator == null) {
semanticValidator = new SemanticValidator(queryContext);
}
return semanticValidator;
}
/**
* Sets
*
* @param jpqlExpression
*/
public final void setJPQLExpression(JPQLExpression jpqlExpression) {
queryContext.setJPQLExpression(jpqlExpression);
}
/**
* Sets the external form of the JPQL query, which will be parsed and information will be
* extracted for later access.
*
* @param query The external form of the JPQL query
*/
protected void setQuery(IQuery query) {
queryContext.setQuery(query);
}
/**
* Validates the query by introspecting it grammatically and semantically.
*
* @return The non-null
list that will be used to store the {@link JPQLQueryProblem
* problems} if any was found
*/
protected List validate() {
List problems = new ArrayList();
validate(getJPQLExpression(), problems);
return problems;
}
/**
* Validates the query by introspecting it grammatically and semantically.
*
* @param jpqlExpression The parsed tree representation of the query
* @param problems A non-null
list that will be used to store the {@link
* JPQLQueryProblem problems} if any was found
*/
public void validate(Expression expression, List problems) {
validateGrammar(expression, problems);
validateSemantic(expression, problems);
}
/**
* Validates the query by only introspecting it grammatically.
*
* @return The non-null
list that will be used to store the {@link JPQLQueryProblem
* problems} if any was found
*/
protected List validateGrammar() {
List problems = new ArrayList();
validateGrammar(getJPQLExpression(), problems);
return problems;
}
/**
* Validates the query by only introspecting it grammatically.
*
* @param expression The parsed tree representation of the query
* @param problems A non-null
list that will be used to store the {@link
* JPQLQueryProblem problems} if any was found
*/
public void validateGrammar(Expression expression, List problems) {
GrammarValidator visitor = grammarValidator();
try {
visitor.setProblems(problems);
expression.accept(visitor);
}
finally {
visitor.dispose();
}
}
/**
* Validates the query by only introspecting it semantically.
*
* @return The non-null
list that will be used to store the {@link JPQLQueryProblem
* problems} if any was found
*/
protected List validateSemantic() {
List problems = new ArrayList();
validateSemantic(getJPQLExpression(), problems);
return problems;
}
/**
* Validates the query by only introspecting it semantically.
*
* @param expression The parsed tree representation of the query
* @param problems A non-null
list that will be used to store the {@link
* JPQLQueryProblem problems} if any was found
*/
public void validateSemantic(Expression expression, List problems) {
SemanticValidator visitor = semanticValidator();
try {
visitor.setProblems(problems);
expression.accept(visitor);
}
finally {
visitor.dispose();
}
}
}