/******************************************************************************* * Copyright (c) 2006, 2015 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.jpql.tools; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.persistence.jpa.jpql.ExpressionTools; import org.eclipse.persistence.jpa.jpql.JPAVersion; import org.eclipse.persistence.jpa.jpql.LiteralType; import org.eclipse.persistence.jpa.jpql.LiteralVisitor; import org.eclipse.persistence.jpa.jpql.ParameterTypeVisitor; import org.eclipse.persistence.jpa.jpql.parser.AbstractTraverseChildrenVisitor; import org.eclipse.persistence.jpa.jpql.parser.AbstractTraverseParentVisitor; import org.eclipse.persistence.jpa.jpql.parser.Expression; import org.eclipse.persistence.jpa.jpql.parser.ExpressionRegistry; import org.eclipse.persistence.jpa.jpql.parser.InputParameter; import org.eclipse.persistence.jpa.jpql.parser.JPQLExpression; import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar; import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectStatement; import org.eclipse.persistence.jpa.jpql.tools.resolver.Declaration; import org.eclipse.persistence.jpa.jpql.tools.resolver.DeclarationResolver; import org.eclipse.persistence.jpa.jpql.tools.resolver.Resolver; import org.eclipse.persistence.jpa.jpql.tools.resolver.ResolverBuilder; import org.eclipse.persistence.jpa.jpql.tools.spi.IManagedTypeProvider; import org.eclipse.persistence.jpa.jpql.tools.spi.IMapping; import org.eclipse.persistence.jpa.jpql.tools.spi.IQuery; import org.eclipse.persistence.jpa.jpql.tools.spi.IType; import org.eclipse.persistence.jpa.jpql.tools.spi.ITypeDeclaration; import org.eclipse.persistence.jpa.jpql.tools.spi.ITypeRepository; /** * This context is used to store information related to the JPQL query. * * <pre><code> {@link IQuery} externalQuery = ...; * * JPQLQueryContext context = new JPQLQueryContext(DefaultJPQLGrammar.instance()); * context.setQuery(query);</code></pre> * * If the JPQL query is already parsed, then the context can use it and it needs to be set before * setting the {@link IQuery}: * <pre><code> {@link JPQLExpression} jpqlExpression = ...; * * JPQLQueryContext context = new JPQLQueryContext(DefaultJPQLGrammar.instance()); * context.setJPQLExpression(jpqlExpression); * context.setQuery(query);</code></pre> * <p> * Provisional API: This interface is part of an interim API that is still under development and * expected to change significantly before reaching stability. It is available at this early stage * to solicit feedback from pioneering adopters on the understanding that any code that uses this * API will almost certainly be broken (repeatedly) as the API evolves. * * @version 2.5 * @since 2.3 * @author Pascal Filion */ @SuppressWarnings("nls") public abstract class JPQLQueryContext { /** * This map caches the {@link JPQLQueryContext contexts} in order to keep them in memory and for * fast access to the information of any query (top-level query and subqueries). */ private Map<Expression, JPQLQueryContext> contexts; /** * The current {@link JPQLQueryContext} is the context used for the current query or subquery. * If the current context is not the global context, then its parent is non <code>null</code>. */ protected JPQLQueryContext currentContext; /** * The parsed {@link Expression JPQL Expression} currently visited. */ private Expression currentQuery; /** * The resolver of the current query's declaration. For a <b>SELECT</b> query, it contains the * information defined in the <b>FROM</b> clause. For <b>DELETE</b> and <b>UPDATE</b> queries, * it contains a single range declaration variable. */ private DeclarationResolver declarationResolver; /** * This visitor is responsible to find the {@link InputParameter} with a specific named parameter * or positional parameter. */ private InputParameterVisitor inputParameterVisitor; /** * The parsed tree representation of the JPQL query. */ private JPQLExpression jpqlExpression; /** * The grammar that defines how to parse a JPQL query. */ private JPQLGrammar jpqlGrammar; /** * This visitor is used to retrieve a variable name from various type of {@link * org.eclipse.persistence.jpa.jpql.parser.Expression JPQL Expression}. */ private LiteralVisitor literalVisitor; /** * This visitor is responsible to calculate the closest type of any input parameter. */ private ParameterTypeVisitor parameterTypeVisitor; /** * When this context is a sub-context used for a subquery, then this is the context for the * parent query. */ protected JPQLQueryContext parent; /** * The external form of the JPQL query being manipulated. */ private IQuery query; /** * This visitor is responsible to retrieve the {@link Expression} that is the beginning of a * query. For a subquery, it will retrieve {@link SimpleSelectStatement} and for a top-level * query, it will retrieve {@link JPQLExpression}. The search goes through the parent hierarchy. */ private QueryExpressionVisitor queryExpressionVisitor; /** * This visitor creates a {@link Resolver} that gives information about the visited {@link * Expression}. The actual {@link Resolver} will calculate the proper {@link IType} as well. */ private ResolverBuilder resolverBuilder; /** * Determines if the parsing system should be tolerant, meaning if it should try to parse invalid * or incomplete queries. */ private boolean tolerant; /** * Internal flag used to determine if the declaration portion of the query was visited. */ private boolean traversed; /** * Creates a new <code>JPQLQueryContext</code>. * * @param jpqlGrammar The {@link JPQLGrammar} defines how to parse a JPQL query */ public JPQLQueryContext(JPQLGrammar jpqlGrammar) { super(); initialize(jpqlGrammar); } /** * Creates a new sub-<code>JPQLQueryContext</code>. * * @param parent The parent context * @param currentQuery The parsed tree representation of the subquery */ protected JPQLQueryContext(JPQLQueryContext parent, Expression currentQuery) { this(parent.jpqlGrammar); store(parent, currentQuery); } protected DeclarationResolver buildDeclarationResolver() { DeclarationResolver parentResolver = (parent != null) ? parent.getDeclarationResolverImp() : null; return buildDeclarationResolver(parentResolver); } protected DeclarationResolver buildDeclarationResolver(DeclarationResolver parent) { return new DeclarationResolver(parent, this); } protected InputParameterVisitor buildInputParameter() { return new InputParameterVisitor(); } protected abstract JPQLQueryContext buildJPQLQueryContext(JPQLQueryContext currentContext, Expression currentQuery); protected abstract LiteralVisitor buildLiteralVisitor(); protected abstract ParameterTypeVisitor buildParameterTypeVisitor(); protected QueryExpressionVisitor buildQueryExpressionVisitor() { return new QueryExpressionVisitor(); } protected abstract ResolverBuilder buildResolverBuilder(); /** * Converts the given {@link Declaration} from being set as a range variable declaration to * a path expression declaration. * <p> * In this query "<code>UPDATE Employee SET firstName = 'MODIFIED' WHERE (SELECT COUNT(m) FROM * managedEmployees m) {@literal >} 0</code>" <em>managedEmployees</em> is an unqualified * collection-valued path expression (<code>employee.managedEmployees</code>). * * @param declaration The {@link Declaration} that was parsed to range over an abstract schema * name but is actually ranging over a path expression */ public void convertUnqualifiedDeclaration(Declaration declaration) { if (parent != null) { // Retrieve the range identification variable from the parent declaration Declaration parentDeclaration = parent.getDeclarationResolverImp().getDeclarations().get(0); String outerVariableName = parentDeclaration.getVariableName(); // Qualify the range expression to be fully qualified getDeclarationResolverImp().convertUnqualifiedDeclaration(declaration, outerVariableName); } } /** * Disposes the internal data. */ public void dispose() { query = null; traversed = false; currentQuery = null; currentContext = this; jpqlExpression = null; contexts.clear(); if (declarationResolver != null) { declarationResolver.dispose(); } } /** * Disposes this context, which is the current context being used by a subquery. Once it is * disposed, any information retrieved will be for the subquery's parent query. */ public void disposeSubqueryContext() { currentContext = currentContext.parent; } /** * Retrieves all the {@link InputParameter InputParameters} with the given parameter name. * * @param parameterName The parameter used to find the {@link InputParameter InputParameters} * with the same value * @return Either the {@link InputParameter InputParameters} that has the given parameter or an * empty collection */ public Collection<InputParameter> findInputParameters(String parameterName) { InputParameterVisitor visitor = getInputParameterVisitor(); try { visitor.parameterName = parameterName; visitor.inputParameters = new ArrayList<InputParameter>(); jpqlExpression.accept(visitor); return visitor.inputParameters; } finally { visitor.parameterName = null; visitor.inputParameters = null; } } /** * Returns the current {@link Expression} being manipulated, which is either the top-level query * or a subquery. * * @return Either the top-level query or a subquery */ public Expression getActualCurrentQuery() { return currentQuery; } /** * Returns the {@link DeclarationResolver} of this context and not from the current query's * declaration. * * @return The {@link DeclarationResolver} for this context */ public DeclarationResolver getActualDeclarationResolver() { return getDeclarationResolverImp(); } /** * Returns the current {@link JPQLQueryContext}, i.e. the context of the query being manipulated, * which can either be the top-level query or a subquery. * * @return The active context */ public JPQLQueryContext getCurrentContext() { return currentContext; } /** * Returns the current {@link Expression} being manipulated, which is either the top-level query * or a subquery. * * @return Either the top-level query or a subquery */ public Expression getCurrentQuery() { return currentContext.currentQuery; } /** * Retrieves the {@link Declaration} for which the given variable name is used to navigate to the * "root" object. This does not go up the hierarchy when looking for the {@link Declaration}. * * @param variableName The name of the identification variable that is used to navigate a "root" object * @return The {@link Declaration} containing the information about the identification variable declaration * @since 2.5 */ public Declaration getDeclaration(String variableName) { return getDeclarationResolver().getDeclaration(variableName); } /** * Returns the {@link DeclarationResolver} of the current query's declaration. For a * <b>SELECT</b> query, it contains the information defined in the <b>FROM</b> clause. For * <b>DELETE</b> and <b>UPDATE</b> queries, it contains a single range declaration variable. If * the current query is a subquery, then it contains the information defined in the * <code>FROM</code> clause. * * @return The {@link DeclarationResolver} for the current query being visited */ public DeclarationResolver getDeclarationResolver() { return currentContext.getDeclarationResolverImp(); } /** * Returns the {@link DeclarationResolver} of the current query's declaration. For a * <b>SELECT</b> query, it contains the information defined in the <b>FROM</b> clause. For * <b>DELETE</b> and <b>UPDATE</b> queries, it contains a single range variable declaration. If * the current query is a subquery, then it contains the information defined in the subquery * <code>FROM</code> clause. * * @param expression The {@link Expression} that will be used to retrieve its query expression, * i.e. either {@link JPQLExpression} or {@link SimpleSelectStatement} * @return The {@link DeclarationResolver} for the current query being visited */ public DeclarationResolver getDeclarationResolver(Expression expression) { // Retrieve the expression for the query (either the top-level query or subquery) // owning the given Expression expression = getQueryExpression(expression); // Retrieve the cached JPQLQueryContext JPQLQueryContext context = contexts.get(expression); if (context != null) { return context.getDeclarationResolverImp(); } // The JPQLQueryContext has not been created yet, // create the parent JPQLQueryContexts first getDeclarationResolver(expression.getParent()); // Create the JPQLQueryContext and DeclarationResolver for the subquery newSubqueryContext(expression); return currentContext.getDeclarationResolverImp(); } /** * Returns the {@link DeclarationResolver} of the current query's declaration. * * @return The {@link DeclarationResolver} for the current query being visited */ protected DeclarationResolver getDeclarationResolverImp() { if (declarationResolver == null) { declarationResolver = buildDeclarationResolver(); } // Only traverse the declaration once if (!traversed) { traversed = true; declarationResolver.populate(currentQuery); } return declarationResolver; } /** * Returns the ordered list of {@link Declaration Declarations}. * * @return The {@link Declaration Declarations} of the current query that was parsed */ public List<Declaration> getDeclarations() { return getDeclarationResolver().getDeclarations(); } /** * Returns the {@link IType} representing the possible given enum type. If the type name * * @param enumTypeName The fully qualified enum type with the constant * @return The external form for the given Enum type */ public IType getEnumType(String enumTypeName) { return getTypeRepository().getEnumType(enumTypeName); } /** * Returns the registry containing the {@link org.eclipse.persistence.jpa.jpql.parser.JPQLQueryBNF * JPQLQueryBNFs} and the {@link org.eclipse.persistence.jpa.jpql.parser.ExpressionFactory * ExpressionFactories} that are used to properly parse a JPQL query. * * @return The registry containing the information related to the JPQL grammar */ public ExpressionRegistry getExpressionRegistry() { return jpqlGrammar.getExpressionRegistry(); } /** * Returns the JPQL grammar that will be used to define how to parse a JPQL query. * * @return The grammar that was used to parse this {@link Expression} */ public JPQLGrammar getGrammar() { return jpqlGrammar; } protected InputParameterVisitor getInputParameterVisitor() { if (inputParameterVisitor == null) { inputParameterVisitor = buildInputParameter(); } return inputParameterVisitor; } /** * Returns the version of the Java Persistence to support, which dictates which version of the * JPQL grammar to support. * * @return The version of the supported Java Persistence functional specification */ public JPAVersion getJPAVersion() { return jpqlGrammar.getJPAVersion(); } /** * Returns the parsed tree representation of the JPQL query. * * @return The parsed tree representation of the JPQL query */ public JPQLExpression getJPQLExpression() { return jpqlExpression; } /** * Returns the string representation of the JPQL query. * * @return The string representation of the JPQL query */ public String getJPQLQuery() { return query.getExpression(); } protected LiteralVisitor getLiteralVisitor() { if (literalVisitor == null) { literalVisitor = buildLiteralVisitor(); } return literalVisitor; } /** * Returns the {@link IMapping} for the field represented by the given {@link Expression}. * * @param expression The {@link Expression} representing a state field path expression or a * collection-valued path expression * @return Either the {@link IMapping} or <code>null</code> if none exists */ public IMapping getMapping(Expression expression) { return getResolver(expression).getMapping(); } /** * Retrieves, if it can be determined, the type of the given {@link InputParameter}. The type * will be guessed based on its location within expression. * <p> * Note: Both named and positional input parameter can be used. * * @param inputParameter The {@link InputParameter} to retrieve its type * @return Either the closest type of the input parameter or <code>null</code> if the type could * not be determined */ public IType getParameterType(InputParameter inputParameter) { ParameterTypeVisitor visitor = getParameterTypeVisitor(); try { inputParameter.accept(visitor); return (IType) visitor.getType(); } finally { visitor.dispose(); } } protected ParameterTypeVisitor getParameterTypeVisitor() { if (parameterTypeVisitor == null) { parameterTypeVisitor = buildParameterTypeVisitor(); } return parameterTypeVisitor; } /** * Returns the parent context if the current context is not the root context. * * @return The parent context or <code>null</code> if the current context is the root */ public JPQLQueryContext getParent() { return parent; } /** * Retrieves the provider of managed types. * * @return The object that has access to the application's managed types. */ public IManagedTypeProvider getProvider() { return query.getProvider(); } /** * Returns the version of the persistence provider. * * @return The version of the persistence provider, if one is extending the default JPQL grammar * defined in the Java Persistence specification, otherwise returns an empty string * @since 2.5 */ public String getProviderVersion() { return jpqlGrammar.getProviderVersion(); } /** * Returns the external form of the JPQL query. * * @return The external form of the JPQL query */ public IQuery getQuery() { return query; } /** * Retrieves the {@link Expression} representing the query statement (either the top-level query * {@link JPQLExpression} or the subquery {@link SimpleSelectStatement}) owning the given {@link * Expression}. * * @param expression A child of the "root" {@link Expression} to retrieve * @return The query statement that is the "root" parent of the given {@link Expression} */ public Expression getQueryExpression(Expression expression) { QueryExpressionVisitor visitor = getQueryExpressionVisitor(); try { expression.accept(visitor); return visitor.expression; } finally { visitor.expression = null; } } protected QueryExpressionVisitor getQueryExpressionVisitor() { if (queryExpressionVisitor == null) { queryExpressionVisitor = buildQueryExpressionVisitor(); } return queryExpressionVisitor; } /** * Creates or retrieved the cached {@link Resolver} for the given {@link Expression}. The * {@link Resolver} can return the {@link IType} and {@link ITypeDeclaration} of the {@link * Expression} and either the {@link org.eclipse.persistence.jpa.jpql.tools.spi.IManagedType IManagedType} * or the {@link IMapping}. * * @param expression The {@link Expression} for which its {@link Resolver} will be retrieved * @return {@link Resolver} for the given {@link Expression} */ public Resolver getResolver(Expression expression) { ResolverBuilder visitor = getResolverBuilder(); try { expression.accept(visitor); return visitor.getResolver(); } finally { visitor.dispose(); } } /** * Retrieves the {@link Resolver} mapped with the given identification variable. If the * identification is not defined in the declaration traversed by this resolver, than the search * will traverse the parent hierarchy. * * @param variableName The identification variable that maps a {@link Resolver} * @return The {@link Resolver} mapped with the given identification variable */ public Resolver getResolver(String variableName) { return getDeclarationResolver().getResolver(variableName); } protected ResolverBuilder getResolverBuilder() { if (resolverBuilder == null) { resolverBuilder = buildResolverBuilder(); } return resolverBuilder; } /** * Returns the variables that got defined in the select expression. This only applies to JPQL * queries built for JPA 2.0. * * @return The variables identifying the select expressions, if any was defined or an empty set * if none were defined */ public Set<String> getResultVariables() { return getDeclarationResolver().getResultVariables(); } /** * Retrieves the external type for the given Java type. * * @param type The Java type to wrap with an external form * @return The external form of the given type */ public IType getType(Class<?> type) { return getTypeRepository().getType(type); } /** * Returns the {@link IType} of the given {@link Expression}. * * @param expression The {@link Expression} for which its type will be calculated * @return Either the {@link IType} that was resolved by this {@link Resolver} or the * {@link IType} for {@link IType#UNRESOLVABLE_TYPE} if it could not be resolved */ public IType getType(Expression expression) { return getResolver(expression).getType(); } /** * Retrieves the external class with the given fully qualified class name. * * @param typeName The fully qualified class name of the class to retrieve * @return The external form of the class to retrieve */ public IType getType(String typeName) { return getTypeRepository().getType(typeName); } /** * Returns the {@link ITypeDeclaration} of the field handled by this {@link Resolver}. * * @param expression The {@link Expression} for which its type declaration will be calculated * @return Either the {@link ITypeDeclaration} that was resolved by this {@link Resolver} or the * {@link ITypeDeclaration} for {@link IType#UNRESOLVABLE_TYPE} if it could not be resolved */ public ITypeDeclaration getTypeDeclaration(Expression expression) { return getResolver(expression).getTypeDeclaration(); } /** * 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} */ public TypeHelper getTypeHelper() { return getTypeRepository().getTypeHelper(); } /** * Returns the type repository for the application. * * @return The repository of {@link IType ITypes} */ public ITypeRepository getTypeRepository() { return getProvider().getTypeRepository(); } /** * Determines whether the JPQL expression has <b>JOIN</b> expressions. * * @return <code>true</code> if the query or subquery being traversed contains <b>JOIN</b> * expressions; <code>false</code> otherwise */ public boolean hasJoins() { return getDeclarationResolver().hasJoins(); } /** * Initializes this {@link JPQLQueryContext}. * * @param jpqlGrammar The grammar that defines how to parse a JPQL query */ protected void initialize(JPQLGrammar jpqlGrammar) { this.tolerant = true; this.currentContext = this; this.jpqlGrammar = jpqlGrammar; this.contexts = new HashMap<Expression, JPQLQueryContext>(); } /** * Initializes the parsed tree representation of the JPQL query if it has not been set before * setting the {@link IQuery}. */ protected void initializeRoot() { if (jpqlExpression == null) { jpqlExpression = new JPQLExpression(query.getExpression(), jpqlGrammar, tolerant); } currentQuery = jpqlExpression; contexts.put(currentQuery, this); } /** * Determines whether the given identification variable is defining a join or a collection member * declaration expressions. * * @param variableName The identification variable to check for what it maps * @return <code>true</code> if the given identification variable maps a collection-valued field * defined in a <code>JOIN</code> or <code>IN</code> expression; <code>false</code> if it's not * defined or it's mapping an abstract schema name */ public boolean isCollectionIdentificationVariable(String variableName) { return getDeclarationResolver().isCollectionIdentificationVariable(variableName); } /** * Determines whether the given variable name is an identification variable name used to define * an abstract schema name. * * @param variableName The name of the variable to verify if it's defined in a range variable * declaration in the current query or any parent query * @return <code>true</code> if the variable name is mapping an abstract schema name; <code>false</code> * if it's defined in a collection member declaration */ public boolean isRangeIdentificationVariable(String variableName) { return getDeclarationResolver().isRangeIdentificationVariable(variableName); } /** * Determines if the given variable is a result variable. * * @param variable The variable to check if it's a result variable * @return <code>true</code> if the given variable is defined as a result variable; <code>false</code> * otherwise */ public boolean isResultVariable(String variable) { return getDeclarationResolverImp().isResultVariable(variable); } /** * Determines whether this {@link JPQLQueryContext} currently holds the information of a subquery * or for the top-level query. * * @return <code>true</code> if the current context is for a subquery; <code>false</code> for the * top-level query * @since 2.5 */ public boolean isSubquery() { return currentContext.parent != null; } /** * Determines if the parser is in tolerant mode or is in fast mode. When the tolerant is turned * on, it means the parser will attempt to parse incomplete or invalid queries. * * @return <code>true</code> if the parsing system should parse invalid or incomplete queries; * <code>false</code> when the query is well-formed and valid * @since 2.5 */ public boolean isTolerant() { return tolerant; } /** * Retrieves the "literal" from the given {@link Expression}. The literal to retrieve depends on * the given {@link LiteralType type}. The literal is basically a string value like an * identification variable name, an input parameter, a path expression, an abstract schema name, * etc. * * @param expression The {@link Expression} to visit * @param type The {@link LiteralType} helps to determine what to retrieve from the visited * {@link Expression} * @return A value from the given {@link Expression} or an empty string if the given {@link * Expression} and the {@link LiteralType} do not match */ public String literal(Expression expression, LiteralType type) { LiteralVisitor visitor = getLiteralVisitor(); try { visitor.setType(type); expression.accept(visitor); return visitor.literal; } finally { visitor.literal = ExpressionTools.EMPTY_STRING; } } /** * Changes the state of this context to use the given subquery. * * @param currentQuery The parsed tree representation of the subquery that will become the * current query * @see #disposeSubqueryContext() */ public void newSubqueryContext(Expression currentQuery) { JPQLQueryContext context = contexts.get(currentQuery); if (context != null) { currentContext = context; } else { currentContext = buildJPQLQueryContext(currentContext, currentQuery); } } /** * Sets the parsed tree representation of the JPQL query. If the expression was parsed outside of * the scope of this context, then this method has to be invoked before {@link #setQuery(IQuery)} * because the JPQL query is automatically parsed by that method. * * @param jpqlExpression The parsed representation of the JPQL query to manipulate * @see #setQuery(IQuery) */ public void setJPQLExpression(JPQLExpression jpqlExpression) { this.jpqlExpression = 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 * @see #setJPQLExpression(JPQLExpression) */ public void setQuery(IQuery query) { this.query = query; initializeRoot(); } /** * Sets whether the parser is in tolerant mode or is in fast mode. When the tolerant is turned * on, it means the parser will attempt to parse incomplete or invalid queries. * <p> * Note: This needs to be set before {@link #setQuery(IQuery)} or {@link #setJPQLExpression(JPQLExpression)} * is invoked. * * @param tolerant Determines if the parsing system should be tolerant, meaning if it should try * to parse invalid or incomplete queries * @since 2.5 */ public void setTolerant(boolean tolerant) { this.tolerant = tolerant; } /** * Stores the information contained in the given parent into this one. * * @param parent The parent context, which is the context of the parent query * @param currentQuery The subquery becoming the current query */ protected void store(JPQLQueryContext parent, Expression currentQuery) { this.parent = parent; // Copy the information from the parent to this one, only a single instance is required this.currentQuery = currentQuery; this.query = parent.query; this.contexts = parent.contexts; this.jpqlExpression = parent.jpqlExpression; this.literalVisitor = parent.literalVisitor; this.resolverBuilder = parent.resolverBuilder; this.parameterTypeVisitor = parent.parameterTypeVisitor; this.queryExpressionVisitor = parent.queryExpressionVisitor; // Cache the context this.contexts.put(currentQuery, this); } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder sb = new StringBuilder(); if (parent != null) { sb.append("Subquery="); } else { sb.append("Top-Level Query="); } sb.append(currentQuery.toParsedText()); return sb.toString(); } /** * This visitor is responsible to find the {@link InputParameter InputParameters} with a certain * parameter name. */ protected class InputParameterVisitor extends AbstractTraverseChildrenVisitor { /** * The collection of {@link InputParameter InputParameters} that was retrieved by traversing the * parsed tree. */ protected Collection<InputParameter> inputParameters; /** * The name of the input parameter to search. */ protected String parameterName; /** * {@inheritDoc} */ @Override public void visit(InputParameter expression) { if (parameterName.equals(expression.getParameter())) { inputParameters.add(expression); } } /** * {@inheritDoc} */ @Override public void visit(SimpleSelectStatement expression) { newSubqueryContext(expression); try { super.visit(expression); } finally { disposeSubqueryContext(); } } } /** * This visitor is responsible to retrieve the {@link Expression} that is the beginning of a * query. For a subquery, it will retrieve {@link SimpleSelectStatement} and for a top-level * query, it will retrieve {@link JPQLExpression}. The search goes through the parent hierarchy. */ protected static class QueryExpressionVisitor extends AbstractTraverseParentVisitor { /** * The {@link Expression} that is the beginning of a query. */ protected Expression expression; /** * {@inheritDoc} */ @Override public void visit(JPQLExpression expression) { this.expression = expression; } /** * {@inheritDoc} */ @Override public void visit(SimpleSelectStatement expression) { this.expression = expression; } } }