| /* |
| * Copyright (c) 2006, 2021 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 v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation |
| // |
| package org.eclipse.persistence.internal.jpa.jpql; |
| |
| import java.lang.reflect.Constructor; |
| import java.security.AccessController; |
| import java.security.PrivilegedActionException; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.expressions.Expression; |
| import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; |
| import org.eclipse.persistence.internal.security.PrivilegedClassForName; |
| import org.eclipse.persistence.internal.security.PrivilegedGetConstructorFor; |
| import org.eclipse.persistence.internal.security.PrivilegedInvokeConstructor; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.jpa.jpql.EclipseLinkLiteralVisitor; |
| import org.eclipse.persistence.jpa.jpql.ExpressionTools; |
| import org.eclipse.persistence.jpa.jpql.LiteralType; |
| import org.eclipse.persistence.jpa.jpql.LiteralVisitor; |
| import org.eclipse.persistence.jpa.jpql.parser.CollectionValuedPathExpression; |
| 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.Join; |
| import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectStatement; |
| import org.eclipse.persistence.jpa.jpql.parser.StateFieldPathExpression; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.mappings.querykeys.QueryKey; |
| import org.eclipse.persistence.queries.DatabaseQuery; |
| import org.eclipse.persistence.queries.ReportQuery; |
| |
| /** |
| * This context is used when creating and populating a {@link DatabaseQuery} when traversing the |
| * parsed representation of a JPQL query. |
| * |
| * @version 2.5 |
| * @since 2.3 |
| * @author Pascal Filion |
| * @author John Bracken |
| */ |
| final class JPQLQueryContext { |
| |
| /** |
| * 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>. |
| */ |
| private JPQLQueryContext currentContext; |
| |
| /** |
| * The parsed {@link Expression JPQL Expression} currently visited. |
| */ |
| private org.eclipse.persistence.jpa.jpql.parser.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; |
| |
| /** |
| * The builder that can convert a {@link org.eclipse.persistence.jpa.jpql.parser.Expression JPQL |
| * Expression} into an {@link Expression EclipseLink Expression}. |
| */ |
| private ExpressionBuilderVisitor expressionBuilder; |
| |
| /** |
| * The cached {@link Expression Expressions} mapped by a variable name. |
| */ |
| private Map<String, Expression> expressions; |
| |
| /** |
| * The input parameter name mapped to its type. The input parameter name starts with the |
| * positional parameter ('?' or ':'). |
| */ |
| Map<InputParameter, Expression> inputParameters; |
| |
| /** |
| * The parsed representation of the JPQL query. |
| */ |
| private JPQLExpression jpqlExpression; |
| |
| /** |
| * The JPQL grammar that defines how to parse a JPQL query. |
| */ |
| private JPQLGrammar jpqlGrammar; |
| |
| /** |
| * The JPQL query to convert into an EclipseLink {@link Expression}. |
| */ |
| private CharSequence jpqlQuery; |
| |
| /** |
| * This visitor is used to retrieve a variable name from various type of |
| * {@link org.eclipse.persistence.jpa.jpql.parser.Expression JPQL Expression}. |
| */ |
| private EclipseLinkLiteralVisitor 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. |
| */ |
| private JPQLQueryContext parent; |
| |
| /** |
| * The {@link DatabaseQuery} being populated by visiting the parsed tree representation of the |
| * JPQL query. |
| */ |
| private DatabaseQuery query; |
| |
| /** |
| * The EclipseLink {@link AbstractSession} this context will use. |
| */ |
| private AbstractSession session; |
| |
| /** |
| * This resolver is used to calculate the type of any |
| * {@link org.eclipse.persistence.jpa.jpql.parser.Expression JPQL Expression}. |
| */ |
| private TypeResolver typeResolver; |
| |
| /** |
| * The types that have been cached for faster access. |
| */ |
| private Map<String, Class<?>> types; |
| |
| /** |
| * The identification variables that were used in the current query. |
| */ |
| private Set<String> usedIdentificationVariables; |
| |
| /** |
| * An empty array used when the type of an |
| * {@link org.eclipse.persistence.jpa.jpql.parser.Expression JPQL Expression} is not required. |
| */ |
| private static final Class<?>[] EMPTY_TYPE = new Class<?>[1]; |
| |
| /** |
| * Creates a new <code>JPQLQueryContext</code>. |
| * |
| * @param query The {@link DatabaseQuery} from which information will be queries |
| * @param jpqlGrammar The JPQL grammar that defines how the JPQL query was parsed |
| * @since 2.5 |
| */ |
| JPQLQueryContext(DatabaseQuery query, JPQLGrammar jpqlGrammar) { |
| this(jpqlGrammar); |
| String jpqlQuery = query.getJPQLString(); |
| cache(query.getSession(), query, parse(jpqlQuery), jpqlQuery); |
| } |
| |
| /** |
| * Creates a new <code>JPQLQueryContext</code>. |
| * |
| * @param jpqlGrammar The JPQL grammar that defines how the JPQL query was parsed |
| */ |
| JPQLQueryContext(JPQLGrammar jpqlGrammar) { |
| super(); |
| this.jpqlGrammar = jpqlGrammar; |
| this.currentContext = this; |
| } |
| |
| /** |
| * Creates a new <code>JPQLQueryContext</code>. |
| * |
| * @param parent The parent of this context, which is never <code>null</code> |
| * @param currentQuery The parsed tree representation of the subquery |
| * @param query The {@link ReportQuery} that will be populated by visiting the parsed tree |
| * representation of the subquery |
| */ |
| private JPQLQueryContext(JPQLQueryContext parent, |
| org.eclipse.persistence.jpa.jpql.parser.Expression currentQuery, |
| ReportQuery query) { |
| |
| this(parent.jpqlGrammar); |
| this.query = query; |
| this.parent = parent; |
| this.currentQuery = currentQuery; |
| } |
| |
| /** |
| * Caches the given input parameter name with its calculated type. |
| * |
| * @param inputParameter The input parameter |
| * @param queryExpression The {@link Expression} that can be reused rather than being recreated |
| */ |
| void addInputParameter(InputParameter inputParameter, Expression queryExpression) { |
| |
| if (parent != null) { |
| parent.addInputParameter(inputParameter, queryExpression); |
| } |
| else { |
| if (inputParameters == null) { |
| inputParameters = new HashMap<>(); |
| } |
| inputParameters.put(inputParameter, queryExpression); |
| } |
| } |
| |
| /** |
| * Caches the given query {@link Expression} for future use. |
| * |
| * @param variableName The identification variable name that is the key to store the given |
| * {@link Expression} |
| * @param expression The {@link Expression} that can be reused rather than being recreated |
| */ |
| void addQueryExpression(String variableName, Expression expression) { |
| currentContext.addQueryExpressionImp(variableName, expression); |
| } |
| |
| /** |
| * Caches the given query {@link Expression} for future use. |
| * |
| * @param variableName The identification variable name that is the key to store the given |
| * {@link Expression} |
| * @param expression The {@link Expression} that can be reused rather than being recreated |
| */ |
| void addQueryExpressionImp(String variableName, Expression expression) { |
| if (expressions == null) { |
| expressions = new HashMap<>(); |
| } |
| expressions.put(variableName, expression); |
| } |
| |
| /** |
| * Adds a virtual range variable declaration that will be used when a JPQL fragment is parsed. |
| * |
| * @param entityName The name of the entity to be accessible with the given variable name |
| * @param variableName The identification variable used to navigate to the entity |
| */ |
| void addRangeVariableDeclaration(String entityName, String variableName) { |
| |
| // This method should only be used by HermesParser.buildSelectionCriteria() |
| declarationResolver = new DeclarationResolver(this, null); |
| |
| declarationResolver.addRangeVariableDeclaration( |
| entityName, |
| variableName |
| ); |
| } |
| |
| /** |
| * Adds the given identification variable, which is done when it's used in a clause, except in |
| * the declaration portion of the query. |
| * |
| * @param variableName The identification variable that is used within a clause |
| */ |
| void addUsedIdentificationVariable(String variableName) { |
| currentContext.addUsedIdentificationVariableImp(variableName); |
| } |
| |
| /** |
| * Adds the given identification variable, which is done when it's used in a clause, except in |
| * the declaration portion of the query. |
| * |
| * @param variableName The identification variable that is used within a clause |
| */ |
| void addUsedIdentificationVariableImp(String variableName) { |
| if (usedIdentificationVariables == null) { |
| usedIdentificationVariables = new HashSet<>(); |
| } |
| usedIdentificationVariables.add(variableName); |
| } |
| |
| /** |
| * Retrieves the Java type for the given type name, which has to be the fully qualified type name. |
| * |
| * @param typeName The fully qualified type name |
| * @return The Java type if it could be retrieved; <code>null</code> otherwise |
| */ |
| private <T> Class<T> attemptLoadType(String typeName) { |
| |
| try { |
| |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| try { |
| return AccessController.doPrivileged( |
| new PrivilegedClassForName<>(typeName, true, getClassLoader()) |
| ); |
| } |
| catch (PrivilegedActionException exception) { |
| return null; |
| } |
| } |
| |
| return PrivilegedAccessHelper.getClassForName(typeName, true, getClassLoader()); |
| } |
| catch (ClassNotFoundException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * Converts the given {@link org.eclipse.persistence.jpa.jpql.parser.Expression JPQL Expression} |
| * into an {@link Expression}. |
| * |
| * @param expression The {@link org.eclipse.persistence.jpa.jpql.parser.Expression JPQL Expression} |
| * to visit and to convert into an {@link Expression} |
| * @return The {@link Expression} representing the given parsed expression |
| */ |
| Expression buildExpression(org.eclipse.persistence.jpa.jpql.parser.Expression expression) { |
| return expressionBuilder().buildExpression(expression, EMPTY_TYPE); |
| } |
| |
| /** |
| * Converts the given {@link org.eclipse.persistence.jpa.jpql.parser.Expression JPQL Expression} |
| * into an {@link Expression}. |
| * |
| * @param expression The {@link org.eclipse.persistence.jpa.jpql.parser.Expression JPQL Expression} |
| * to visit and to convert into an {@link Expression} |
| * @param type The given array will be used to store the type of the given expression |
| * @return The {@link Expression} representing the given parsed expression |
| */ |
| Expression buildExpression(org.eclipse.persistence.jpa.jpql.parser.Expression expression, |
| Class<?>[] type) { |
| |
| return expressionBuilder().buildExpression(expression, type); |
| } |
| |
| /** |
| * Creates a new EclipseLink {@link Expression} by visiting the given JPQL {@link |
| * CollectionValuedPathExpression} that is used in the <code><b>GROUP BY</b></code> clause. |
| * |
| * @param expression The {@link CollectionValuedPathExpression} to convert into an EclipseLink |
| * {@link Expression} |
| * @return The EclipseLink {@link Expression} representation of that path expression |
| */ |
| Expression buildGroupByExpression(CollectionValuedPathExpression expression) { |
| return expressionBuilder().buildGroupByExpression(expression); |
| } |
| |
| /** |
| * Creates a new EclipseLink {@link Expression} by visiting the given JPQL {@link |
| * StateFieldPathExpression}. This method temporarily changes the null allowed flag if the state |
| * field is a foreign reference mapping |
| * |
| * @param expression The {@link StateFieldPathExpression} to convert into an EclipseLink {@link |
| * Expression} |
| * @return The EclipseLink {@link Expression} representation of that path expression |
| */ |
| Expression buildModifiedPathExpression(StateFieldPathExpression expression) { |
| return expressionBuilder().buildModifiedPathExpression(expression); |
| } |
| |
| /** |
| * Creates a new {@link ReportQuery} by visiting the given {@link SimpleSelectStatement}. |
| * |
| * @param expression The {@link SimpleSelectStatement} to convert into a {@link ReportQuery} |
| * @return A fully initialized {@link ReportQuery} |
| */ |
| ReportQuery buildSubquery(SimpleSelectStatement expression) { |
| return expressionBuilder().buildSubquery(expression); |
| } |
| |
| /** |
| * Sets the given named query and string representation of the JPQL query. |
| * |
| * @param session The EclipseLink {@link AbstractSession} this context will use |
| * @param query The {@link DatabaseQuery} that may already exist and it will be populated with |
| * the information contained in the JPQL query or <code>null</code> if the query will need to be created |
| * @param jpqlExpression The parsed tree representation of the JPQL query |
| * @param jpqlQuery The JPQL query |
| */ |
| void cache(AbstractSession session, |
| DatabaseQuery query, |
| JPQLExpression jpqlExpression, |
| CharSequence jpqlQuery) { |
| |
| this.query = query; |
| this.session = session; |
| this.jpqlQuery = jpqlQuery; |
| this.currentQuery = jpqlExpression; |
| this.jpqlExpression = jpqlExpression; |
| } |
| |
| /** |
| * Returns the type of the given {@link DatabaseMapping}, which is the persistent field type. |
| * |
| * @param mapping The {@link DatabaseMapping} to retrieve its persistent field type |
| * @return The persistent field type |
| */ |
| Class<?> calculateMappingType(DatabaseMapping mapping) { |
| return typeResolver().calculateMappingType(mapping); |
| } |
| |
| /** |
| * Returns the type of the given {@link QueryKey}, which is the persistent field type. |
| * |
| * @param queryKey The {@link QueryKey} to retrieve its persistent field type |
| * @return The persistent field type |
| */ |
| Class<?> calculateQueryKeyType(QueryKey queryKey) { |
| return typeResolver().calculateQueryKeyType(queryKey); |
| } |
| |
| /** |
| * 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. |
| */ |
| void disposeSubqueryContext() { |
| currentContext = currentContext.parent; |
| } |
| |
| private ExpressionBuilderVisitor expressionBuilder() { |
| |
| if (parent != null) { |
| return parent.expressionBuilder(); |
| } |
| |
| if (expressionBuilder == null) { |
| expressionBuilder = new ExpressionBuilderVisitor(this); |
| } |
| |
| return expressionBuilder; |
| } |
| |
| /** |
| * Retrieves the {@link Declaration} for which the given variable name is used to navigate to the |
| * "root" object. This goes up the hierarchy when looking for the {@link Declaration} if it's not |
| * defined in the current context. |
| * |
| * @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 |
| * @see #getDeclaration(String) |
| */ |
| Declaration findDeclaration(String variableName) { |
| return currentContext.findDeclarationImp(variableName); |
| } |
| |
| Declaration findDeclarationImp(String variableName) { |
| Declaration declaration = getDeclarationResolverImp().getDeclaration(variableName); |
| if ((declaration == null) && (parent != null)) { |
| declaration = parent.findDeclarationImp(variableName); |
| } |
| return declaration; |
| } |
| |
| /** |
| * Retrieves the cached {@link Expression} associated with the given variable name. The scope of |
| * the search is the entire query (going from the current query, if it's a subquery, up to the |
| * top-level query). |
| * |
| * @param variableName The variable name for which its associated {@link Expression} is requested |
| * @return The cached {@link Expression} associated with the given variable name or <code>null</code> |
| * if none was cached. |
| */ |
| Expression findQueryExpression(String variableName) { |
| return currentContext.findQueryExpressionImp(variableName); |
| } |
| |
| Expression findQueryExpressionImp(String variableName) { |
| Expression expression = getQueryExpressionImp(variableName); |
| if ((expression == null) && (parent != null)) { |
| expression = parent.findQueryExpressionImp(variableName); |
| } |
| return expression; |
| } |
| |
| /** |
| * 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 |
| */ |
| JPQLQueryContext getActualParent() { |
| return parent; |
| } |
| |
| /** |
| * Returns the root {@link Expression} for which all new {@link Expression expressions} a child. |
| * |
| * @return The root {@link Expression} of the query or subquery |
| */ |
| Expression getBaseExpression() { |
| // Retrieve the first declaration, which is the base declaration (For top-level query, it's |
| // always a range over an abstract schema name. For subqueries, it's either a range over an |
| // abstract schema name or a derived path expression) |
| return getDeclarationResolver().getFirstDeclaration().getQueryExpression(); |
| } |
| |
| /** |
| * Returns the {@link ClassLoader} used by EclipseLink to load the application's classes. |
| * |
| * @return The application's {@link ClassLoader} |
| */ |
| private ClassLoader getClassLoader() { |
| return getSession().getDatasourcePlatform().getConversionManager().getLoader(); |
| } |
| |
| /** |
| * Returns the {@link Constructor} for the given Java {@link Class}. The constructor to retrieve |
| * has the given parameter types. |
| * |
| * @param type The Java type for which its constructor should be retrieved |
| * @param parameterTypes The types of the constructor's parameters |
| * @return The {@link Constructor} or <code>null</code> if none exist or the privilege access |
| * was denied |
| */ |
| <T> Constructor<T> getConstructor(Class<T> type, Class<?>[] parameterTypes) { |
| try { |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| try { |
| return AccessController.doPrivileged( |
| new PrivilegedGetConstructorFor<>(type, parameterTypes, true) |
| ); |
| } |
| catch (PrivilegedActionException exception) { |
| return null; |
| } |
| } |
| |
| return PrivilegedAccessHelper.getConstructorFor(type, parameterTypes, true); |
| } |
| catch (NoSuchMethodException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * 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 |
| */ |
| 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 |
| */ |
| org.eclipse.persistence.jpa.jpql.parser.Expression getCurrentQuery() { |
| return currentContext.currentQuery; |
| } |
| |
| /** |
| * Returns the current {@link DatabaseQuery} being populated. |
| * |
| * @return The {@link DatabaseQuery} being populated, which can be the top-level query or a |
| * subquery depending on which query is been visited |
| */ |
| @SuppressWarnings("unchecked") |
| <T extends DatabaseQuery> T getDatabaseQuery() { |
| return (T) currentContext.query; |
| } |
| |
| /** |
| * 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 |
| * @see #findDeclaration(String) |
| */ |
| 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 |
| */ |
| DeclarationResolver getDeclarationResolver() { |
| return currentContext.getDeclarationResolverImp(); |
| } |
| |
| /** |
| * Returns the {@link DeclarationResolver} of the current query's declaration. |
| * |
| * @return The {@link DeclarationResolver} for the current query being visited |
| */ |
| DeclarationResolver getDeclarationResolverImp() { |
| |
| if (declarationResolver == null) { |
| DeclarationResolver parentResolver = (parent == null) ? null : parent.getDeclarationResolverImp(); |
| declarationResolver = new DeclarationResolver(this, parentResolver); |
| } |
| |
| 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 |
| */ |
| List<Declaration> getDeclarations() { |
| return getDeclarationResolver().getDeclarations(); |
| } |
| |
| /** |
| * Retrieves the descriptor associated with the given type. |
| * |
| * @param type The type associated with the descriptor |
| * @return The EclipseLink {@link ClassDescriptor descriptor} representing the managed type |
| */ |
| ClassDescriptor getDescriptor(Class<?> type) { |
| return getSession().getClassDescriptor(type); |
| } |
| |
| /** |
| * Retrieves the descriptor associated with the given abstract schema name, which is the |
| * descriptor's alias. |
| * |
| * @param abstractSchemaName The managed type name associated with the managed type |
| * @return The EclipseLink {@link ClassDescriptor descriptor} representing the managed type |
| */ |
| ClassDescriptor getDescriptor(String abstractSchemaName) { |
| return getSession().getDescriptorForAlias(abstractSchemaName); |
| } |
| |
| /** |
| * Returns the {@link Class} representing the possible given enum constant. If the given value |
| * does not represent an enum constant, then <code>null</code> is returned. |
| * |
| * @param enumTypeName The fully qualified enum type with the constant |
| * @return The external form for the given Enum type or <code>null</code> if none exists |
| */ |
| Class<?> getEnumType(String enumTypeName) { |
| |
| // Get the position of the last dot so we can remove the constant |
| int lastDotIndex = enumTypeName.lastIndexOf('.'); |
| |
| if (lastDotIndex == -1) { |
| return null; |
| } |
| |
| // Retrieve the fully qualified enum type name |
| String typeName = enumTypeName.substring(0, lastDotIndex); |
| |
| // Attempt to load the enum type |
| Class<?> type = getType(typeName); |
| return ((type != null) && type.isEnum()) ? type : null; |
| } |
| |
| /** |
| * Returns the first {@link Declaration} of the current context that was created after visiting |
| * the declaration clause. |
| * |
| * @return The first {@link Declaration} object |
| */ |
| Declaration getFirstDeclaration() { |
| return currentContext.getFirstDeclarationImp(); |
| } |
| |
| /** |
| * Returns the first {@link Declaration}, from this context, that was created after visiting the |
| * declaration clause. |
| * |
| * @return The first {@link Declaration} object |
| */ |
| Declaration getFirstDeclarationImp() { |
| return getDeclarationResolverImp().getFirstDeclaration(); |
| } |
| |
| /** |
| * Returns the {@link JPQLGrammar} that defines how the JPQL query was parsed. |
| * |
| * @return The {@link JPQLGrammar} that was used to parse the JPQL query |
| */ |
| JPQLGrammar getGrammar() { |
| return jpqlGrammar; |
| } |
| |
| /** |
| * Returns the parsed representation of a <b>JOIN FETCH</b> that were defined in the same |
| * declaration than the given range identification variable name. |
| * |
| * @param variableName The name of the identification variable that should be used to define an |
| * abstract schema name |
| * @return The <b>JOIN FETCH</b> expressions used in the same declaration or an empty collection |
| * if none was defined |
| */ |
| Collection<Join> getJoinFetches(String variableName) { |
| return getDeclarationResolver().getJoinFetches(variableName); |
| } |
| |
| /** |
| * Returns the parsed tree representation of the JPQL query. |
| * |
| * @return The parsed tree representation of the JPQL query |
| */ |
| JPQLExpression getJPQLExpression() { |
| return (parent != null) ? parent.getJPQLExpression() : jpqlExpression; |
| } |
| |
| /** |
| * Returns the string representation of the JPQL query. |
| * |
| * @return The string representation of the JPQL query |
| */ |
| CharSequence getJPQLQuery() { |
| return (parent != null) ? parent.getJPQLQuery() : jpqlQuery; |
| } |
| |
| /** |
| * 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 |
| */ |
| Class<?> getParameterType(InputParameter inputParameter) { |
| ParameterTypeVisitor resolver = parameterTypeVisitor(); |
| try { |
| inputParameter.accept(resolver); |
| return resolver.getType(); |
| } |
| finally { |
| resolver.dispose(); |
| } |
| } |
| |
| /** |
| * 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 |
| */ |
| JPQLQueryContext getParent() { |
| return currentContext.parent; |
| } |
| |
| /** |
| * Retrieves the cached {@link Expression} associated with the given variable name. The scope of |
| * the search is local to the current query or subquery. |
| * |
| * @param variableName The variable name for which its associated {@link Expression} is requested |
| * @return The cached {@link Expression} associated with the given variable name or <code>null</code> |
| * if none was cached. |
| */ |
| Expression getQueryExpression(String variableName) { |
| return currentContext.getQueryExpressionImp(variableName); |
| } |
| |
| /** |
| * Retrieves the cached {@link Expression} associated with the given variable name. |
| * |
| * @param variableName The variable name for which its associated {@link Expression} is requested |
| * @return The cached {@link Expression} associated with the given variable name or <code>null</code> |
| * if none was cached. |
| */ |
| Expression getQueryExpressionImp(String variableName) { |
| return (expressions == null) ? null : expressions.get(variableName); |
| } |
| |
| /** |
| * Returns the EclipseLink {@link AbstractSession} that this query will execute against, which |
| * gives access to the JPA artifacts. |
| * |
| * @return The current EclipseLink session |
| */ |
| AbstractSession getSession() { |
| return (parent != null) ? parent.getSession() : session; |
| } |
| |
| /** |
| * Returns the Java class of the given {@link Expression}. |
| * |
| * @param expression The {@link Expression} for which its type will be calculated |
| * @return Either the Java class that was resolved or the <code>null</code> if it could not be resolved |
| */ |
| Class<?> getType(org.eclipse.persistence.jpa.jpql.parser.Expression expression) { |
| return typeResolver().resolve(expression); |
| } |
| |
| /** |
| * Retrieves the external class for 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 |
| */ |
| Class<?> getType(String typeName) { |
| return loadTypeImp(typeName); |
| } |
| |
| private Map<String, Class<?>> getTypes() { |
| |
| if (parent != null) { |
| return parent.getTypes(); |
| } |
| |
| if (types == null) { |
| types = new HashMap<>(); |
| } |
| |
| return types; |
| } |
| |
| /** |
| * Returns the identification variables that was used in the current query. |
| * |
| * @return The identification variables that were used throughout the current query at the |
| * exception of their declaration |
| */ |
| Set<String> getUsedIdentificationVariables() { |
| return currentContext.getUsedIdentificationVariablesImp(); |
| } |
| |
| /** |
| * Returns the identification variables that are registered in this context. |
| * |
| * @return The identification variables that were used throughout the current query at the |
| * exception of their declaration |
| */ |
| Set<String> getUsedIdentificationVariablesImp() { |
| if (usedIdentificationVariables == null) { |
| return Collections.emptySet(); |
| } |
| return usedIdentificationVariables; |
| } |
| |
| /** |
| * 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 |
| */ |
| boolean isCollectionIdentificationVariable(String variableName) { |
| return getDeclarationResolver().isCollectionIdentificationVariable(variableName); |
| } |
| |
| /** |
| * Determines whether the given identification variable is used, at the exception of where it was |
| * defined. |
| * |
| * @param variableName The identification variable to check if it was used in a clause |
| * @return <code>true</code> if the identification is used in a clause, at the exception of where |
| * it was defined; <code>false</code> otherwise |
| */ |
| boolean isIdentificationVariableUsed(String variableName) { |
| return currentContext.isIdentificationVariableUsedImp(variableName); |
| } |
| |
| private boolean isIdentificationVariableUsedImp(String variableName) { |
| boolean result = (usedIdentificationVariables != null) && usedIdentificationVariables.contains(variableName); |
| if (!result && (parent != null)) { |
| result = parent.isIdentificationVariableUsedImp(variableName); |
| } |
| return result; |
| } |
| |
| /** |
| * 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 |
| */ |
| boolean isRangeIdentificationVariable(String variableName) { |
| return getDeclarationResolverImp().isRangeIdentificationVariable(variableName); |
| } |
| |
| /** |
| * Determines if the given variable is a result variable. |
| * |
| * @param variableName 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 |
| */ |
| boolean isResultVariable(String variableName) { |
| return getDeclarationResolverImp().isResultVariable(variableName); |
| } |
| |
| /** |
| * 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 |
| */ |
| String literal(org.eclipse.persistence.jpa.jpql.parser.Expression expression, LiteralType type) { |
| |
| LiteralVisitor visitor = literalVisitor(); |
| |
| try { |
| visitor.setType(type); |
| expression.accept(visitor); |
| |
| // Make sure the identification variable is capitalized and unique since |
| // String.equals() is used rather than String.equalsIgnoreCase() |
| if (visitor.literal != ExpressionTools.EMPTY_STRING && |
| (type == LiteralType.IDENTIFICATION_VARIABLE || |
| type == LiteralType.RESULT_VARIABLE|| |
| type == LiteralType.PATH_EXPRESSION_IDENTIFICATION_VARIABLE)) { |
| |
| return visitor.literal.toUpperCase().intern(); |
| } |
| |
| return visitor.literal; |
| } |
| finally { |
| visitor.literal = ExpressionTools.EMPTY_STRING; |
| } |
| } |
| |
| private LiteralVisitor literalVisitor() { |
| |
| if (parent != null) { |
| return parent.literalVisitor(); |
| } |
| |
| if (literalVisitor == null) { |
| literalVisitor = new EclipseLinkLiteralVisitor(); |
| } |
| |
| return literalVisitor; |
| } |
| |
| private Class<?> loadInnerType(String typeName) { |
| |
| int index = typeName.lastIndexOf('.'); |
| |
| if (index == -1) { |
| return null; |
| } |
| |
| StringBuilder sb = new StringBuilder(); |
| sb.append(typeName.substring(0, index)); |
| sb.append("$"); |
| sb.append(typeName.substring(index + 1)); |
| typeName = sb.toString(); |
| |
| return loadTypeImp(typeName); |
| } |
| |
| private Class<?> loadTypeImp(String typeName) { |
| |
| Map<String, Class<?>> types = getTypes(); |
| Class<?> type = types.get(typeName); |
| |
| // The type was already cached, simply return it |
| if (type != null) { |
| return type; |
| } |
| |
| // Attempt to load the Java type |
| Class<?> javaType = attemptLoadType(typeName); |
| |
| // A Java type exists, return it |
| if (javaType != null) { |
| types.put(javaType.getName(), javaType); |
| return javaType; |
| } |
| |
| // Now try with a possible inner enum type |
| return loadInnerType(typeName); |
| } |
| |
| /** |
| * Creates a new instance from the given Java type. |
| * |
| * @param type The Java type for which a new instance will be created |
| * @param parameterType The type of the constructor's single parameter |
| * @param parameter The object to pass during instantiation |
| * @return A new instance or <code>null</code> if a problem was encountered during instantiation |
| */ |
| <T> T newInstance(Class<T> type, Class<?> parameterType, Object parameter) { |
| return newInstance(type, new Class<?>[] { parameterType }, new Object[] { parameter }); |
| } |
| |
| /** |
| * Creates a new instance from the given Java type. |
| * |
| * @param type The Java type for which a new instance will be created |
| * @param parameterTypes The types of the constructor's parameters |
| * @param parameters The objects to pass during instantiation |
| * @return A new instance of the given type or <code>null</code> if a problem was encountered |
| * during instantiation |
| */ |
| <T> T newInstance(Class<T> type, Class<?>[] parameterTypes, Object[] parameters) { |
| |
| Constructor<T> constructor = getConstructor(type, parameterTypes); |
| |
| if (constructor != null) { |
| return newInstance(constructor, parameters); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Creates a new instance by using the given constructor. |
| * |
| * @param constructor The constructor used to create the new instance |
| * @param parameters The objects to pass during instantiation |
| * @return A new instance or <code>null</code> if a problem was encountered during instantiation |
| */ |
| <T> T newInstance(Constructor<T> constructor, Object[] parameters) { |
| |
| try { |
| |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| try { |
| return AccessController.doPrivileged( |
| new PrivilegedInvokeConstructor<>(constructor, parameters) |
| ); |
| } |
| catch (PrivilegedActionException exception) { |
| return null; |
| } |
| } |
| |
| return PrivilegedAccessHelper.invokeConstructor(constructor, parameters); |
| } |
| catch (ReflectiveOperationException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * Changes the state of this context to use the given subquery. Once the subquery is created and |
| * populated, {@link #disposeSubqueryContext()} has to be called. |
| * |
| * @param currentQuery The parsed tree representation of the subquery that will become the |
| * current {@link DatabaseQuery} |
| * @param query The {@link ReportQuery} for the subquery |
| * @see #disposeSubqueryContext() |
| */ |
| void newSubQueryContext(org.eclipse.persistence.jpa.jpql.parser.Expression currentQuery, |
| ReportQuery query) { |
| |
| currentContext = new JPQLQueryContext(currentContext, currentQuery, query); |
| } |
| |
| private ParameterTypeVisitor parameterTypeVisitor() { |
| |
| if (parent != null) { |
| return parent.parameterTypeVisitor(); |
| } |
| |
| if (parameterTypeVisitor == null) { |
| parameterTypeVisitor = new ParameterTypeVisitor(this); |
| } |
| |
| return parameterTypeVisitor; |
| } |
| |
| private JPQLExpression parse(String jpqlQuery) { |
| return new JPQLExpression(jpqlQuery, jpqlGrammar); |
| } |
| |
| /** |
| * Resolves the given {@link org.eclipse.persistence.jpa.jpql.parser.Expression Expression} and |
| * returns the corresponding descriptor. Usually this method is used when retrieving the |
| * descriptor for an identification variable or a path expression. In the case of a range |
| * variable declaration, the descriptor of the entity is returned. In the case of a path |
| * expression, the mapping reference descriptor will be returned. |
| * |
| * @param expression The {@link org.eclipse.persistence.jpa.jpql.parser.Expression Expression} |
| */ |
| ClassDescriptor resolveDescriptor(org.eclipse.persistence.jpa.jpql.parser.Expression expression) { |
| return typeResolver().resolveDescriptor(expression); |
| } |
| |
| /** |
| * Resolves the given {@link org.eclipse.persistence.jpa.jpql.parser.Expression Expression} and |
| * returns the {@link DatabaseMapping}. |
| * |
| * @param expression The {@link org.eclipse.persistence.jpa.jpql.parser.Expression Expression} to |
| * resolve by traversing its path expression |
| * @return The {@link DatabaseMapping} that is representing the last path or <code>null</code> if |
| * the path expression could not be resolved |
| */ |
| DatabaseMapping resolveMapping(org.eclipse.persistence.jpa.jpql.parser.Expression expression) { |
| return typeResolver().resolveMapping(expression); |
| } |
| |
| /** |
| * Resolves the given {@link org.eclipse.persistence.jpa.jpql.parser.Expression Expression} and |
| * either returns the {@link DatabaseMapping} or the {@link QueryKey} object. |
| * |
| * @param expression The {@link org.eclipse.persistence.jpa.jpql.parser.Expression Expression} to |
| * resolve by traversing its path expression |
| * @return Either the {@link DatabaseMapping} or the {@link QueryKey} that is representing the |
| * last path or <code>null</code> if the path expression could not be resolved |
| */ |
| Object resolveMappingObject(org.eclipse.persistence.jpa.jpql.parser.Expression expression) { |
| return typeResolver().resolveMappingObject(expression); |
| } |
| |
| /** |
| * Sets the query to populate. |
| * |
| * @param query The {@link DatabaseQuery} that was created before populating it |
| */ |
| void setDatabasQuery(DatabaseQuery query) { |
| this.query = query; |
| } |
| |
| TypeResolver typeResolver() { |
| |
| if (parent != null) { |
| return parent.typeResolver(); |
| } |
| |
| if (typeResolver == null) { |
| typeResolver = new TypeResolver(this); |
| } |
| |
| return typeResolver; |
| } |
| } |