| /* |
| * 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.jpa.jpql.tools.resolver; |
| |
| import java.sql.Date; |
| import java.sql.Time; |
| import java.sql.Timestamp; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import org.eclipse.persistence.jpa.jpql.Assert; |
| import org.eclipse.persistence.jpa.jpql.ExpressionTools; |
| import org.eclipse.persistence.jpa.jpql.LiteralType; |
| import org.eclipse.persistence.jpa.jpql.parser.AbsExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.AbstractExpressionVisitor; |
| import org.eclipse.persistence.jpa.jpql.parser.AbstractPathExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.AbstractSchemaName; |
| import org.eclipse.persistence.jpa.jpql.parser.AdditionExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.AllOrAnyExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.AndExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.ArithmeticExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.ArithmeticFactor; |
| import org.eclipse.persistence.jpa.jpql.parser.AvgFunction; |
| import org.eclipse.persistence.jpa.jpql.parser.BadExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.BetweenExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.CaseExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.CoalesceExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.CollectionExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.CollectionMemberDeclaration; |
| import org.eclipse.persistence.jpa.jpql.parser.CollectionMemberExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.CollectionValuedPathExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.ComparisonExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.ConcatExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.CountFunction; |
| import org.eclipse.persistence.jpa.jpql.parser.DateTime; |
| import org.eclipse.persistence.jpa.jpql.parser.DeleteClause; |
| import org.eclipse.persistence.jpa.jpql.parser.DeleteStatement; |
| import org.eclipse.persistence.jpa.jpql.parser.DivisionExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.EmptyCollectionComparisonExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.EntityTypeLiteral; |
| import org.eclipse.persistence.jpa.jpql.parser.EntryExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.ExistsExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.Expression; |
| import org.eclipse.persistence.jpa.jpql.parser.ExpressionVisitor; |
| import org.eclipse.persistence.jpa.jpql.parser.FromClause; |
| import org.eclipse.persistence.jpa.jpql.parser.FunctionExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.GroupByClause; |
| import org.eclipse.persistence.jpa.jpql.parser.HavingClause; |
| import org.eclipse.persistence.jpa.jpql.parser.IdentificationVariable; |
| import org.eclipse.persistence.jpa.jpql.parser.IdentificationVariableDeclaration; |
| import org.eclipse.persistence.jpa.jpql.parser.InExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.IndexExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.InputParameter; |
| import org.eclipse.persistence.jpa.jpql.parser.JPQLExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.Join; |
| import org.eclipse.persistence.jpa.jpql.parser.KeyExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.KeywordExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.LengthExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.LikeExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.LocateExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.LowerExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.MaxFunction; |
| import org.eclipse.persistence.jpa.jpql.parser.MinFunction; |
| import org.eclipse.persistence.jpa.jpql.parser.ModExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.MultiplicationExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.NotExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.NullComparisonExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.NullExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.NullIfExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.NumericLiteral; |
| import org.eclipse.persistence.jpa.jpql.parser.ObjectExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.OnClause; |
| import org.eclipse.persistence.jpa.jpql.parser.OrExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.OrderByClause; |
| import org.eclipse.persistence.jpa.jpql.parser.OrderByItem; |
| import org.eclipse.persistence.jpa.jpql.parser.RangeVariableDeclaration; |
| import org.eclipse.persistence.jpa.jpql.parser.ResultVariable; |
| import org.eclipse.persistence.jpa.jpql.parser.SelectClause; |
| import org.eclipse.persistence.jpa.jpql.parser.SelectStatement; |
| import org.eclipse.persistence.jpa.jpql.parser.SimpleFromClause; |
| import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectClause; |
| import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectStatement; |
| import org.eclipse.persistence.jpa.jpql.parser.SizeExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.SqrtExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.StateFieldPathExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.StringLiteral; |
| import org.eclipse.persistence.jpa.jpql.parser.SubExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.SubstringExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.SubtractionExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.SumFunction; |
| import org.eclipse.persistence.jpa.jpql.parser.TreatExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.TrimExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.TypeExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.UnknownExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.UpdateClause; |
| import org.eclipse.persistence.jpa.jpql.parser.UpdateItem; |
| import org.eclipse.persistence.jpa.jpql.parser.UpdateStatement; |
| import org.eclipse.persistence.jpa.jpql.parser.UpperExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.ValueExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.WhenClause; |
| import org.eclipse.persistence.jpa.jpql.parser.WhereClause; |
| import org.eclipse.persistence.jpa.jpql.tools.JPQLQueryContext; |
| import org.eclipse.persistence.jpa.jpql.tools.spi.IType; |
| |
| /** |
| * 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. |
| * <p> |
| * The type of an {@link Expression} follows the following rules: |
| * <ul> |
| * <li>The type of the query result specified by the <b>SELECT</b> clause of a query is an entity |
| * abstract schema type, a state field type, the result of a scalar expression, the result of an |
| * aggregate function, the result of a construction operation, or some sequence of these.</li> |
| * |
| * <li>The result type of the <b>SELECT</b> clause is defined by the result types of the select |
| * expressions contained in it. When multiple select expressions are used in the <b>SELECT</b> |
| * clause, the elements in this result correspond in order to the order of their specification in |
| * the <b>SELECT</b> clause and in type to the result types of each of the select expressions.</li> |
| * |
| * <li>The result type of an <code>identification_variable</code> is the type of the entity object |
| * or embeddable object to which the identification variable corresponds. The type of an |
| * <code>identification_variable</code> that refers to an entity abstract schema type is the type of |
| * the entity to which that identification variable corresponds or a subtype as determined by the |
| * object/relational mapping.</li> |
| * |
| * <li>The result type of a <code>single_valued_path_expression</code> that is a |
| * <code>state_field_path_expression</code> is the same type as the corresponding state field of the |
| * entity or embeddable class. If the state field of the entity is a primitive type, the result type |
| * is the corresponding object type.</li> |
| * |
| * <li>The result type of a <code>single_valued_path_expression</code> that is a |
| * <code>single_valued_object_path_expression</code> is the type of the entity object or embeddable |
| * object to which the path expression corresponds. A |
| * <code>single_valued_object_path_expression</code> that results in an entity object will result in |
| * an entity of the type of the relationship field or the subtype of the relationship field of the |
| * entity object as determined by the object/relational mapping.</li> |
| * |
| * <li>The result type of a <code>single_valued_path_expression</code> that is an |
| * identification_variable to which the <code>KEY</code> or <code>VALUE</code> function has been |
| * applied is determined by the type of the map key or value respectively, as defined by the above |
| * rules.</li> |
| * |
| * <li>The result type of a <code>single_valued_path_expression</code> that is an |
| * identification_variable to which the <code>ENTRY</code> function has been applied is |
| * {@link java.util.Map.Entry Map.Entry}, where the key and value types of the map entry are |
| * determined by the above rules as applied to the map key and map value respectively.</li> |
| * |
| * <li>The result type of a <code>scalar_expression</code> is the type of the scalar value to which |
| * the expression evaluates.</li> |
| * |
| * <li>The result type of an <code>entity_type_expression</code> scalar expression is the Java class |
| * to which the resulting abstract schema type corresponds.</li> |
| * |
| * <li>The result type of a <code>constructor_expression</code> is the type of the class for which |
| * the constructor is defined. The types of the arguments to the constructor are defined by the |
| * above rules.</li> |
| * </ul> |
| * <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.1 |
| * @since 2.3 |
| * @author Pascal Filion |
| */ |
| @SuppressWarnings("nls") |
| public abstract class ResolverBuilder implements ExpressionVisitor { |
| |
| /** |
| * This visitor is responsible to retrieve the {@link CollectionExpression} if it is visited. |
| */ |
| private CollectionExpressionVisitor collectionExpressionVisitor; |
| |
| /** |
| * The context used to query information about the JPQL query. |
| */ |
| private final JPQLQueryContext queryContext; |
| |
| /** |
| * The {@link Resolver} for the {@link Expression} that was visited. |
| */ |
| protected Resolver resolver; |
| |
| /** |
| * Creates a new <code>ResolverBuilder</code>. |
| * |
| * @param queryContext The context used to query information about the JPQL query |
| * @exception NullPointerException The {@link JPQLQueryContext} cannot be <code>null</code> |
| */ |
| public ResolverBuilder(JPQLQueryContext queryContext) { |
| super(); |
| Assert.isNotNull(queryContext, "The JPQLQueryContext cannot be null"); |
| this.queryContext = queryContext; |
| } |
| |
| /** |
| * Creates a new {link Resolver} that simply wraps the already determined type by using its |
| * fully qualified class name. |
| * |
| * @param typeName The fully qualified class name of the Java type to wrap with a {@link Resolver} |
| * @return A new {@link Resolver} |
| */ |
| protected Resolver buildClassNameResolver(String typeName) { |
| return new ClassNameResolver(getDeclarationResolver(), typeName); |
| } |
| |
| /** |
| * Creates a new {link Resolver} that simply wraps the already determined type. |
| * |
| * @param type The Java type to wrap with a {@link Resolver} |
| * @return A new {@link Resolver} |
| */ |
| protected Resolver buildClassResolver(Class<?> type) { |
| return new ClassResolver(getDeclarationResolver(), type); |
| } |
| |
| /** |
| * Creates a visitor that collects the {@link CollectionExpression} if it's been visited. |
| * |
| * @return A new {@link CollectionExpressionVisitor} |
| */ |
| protected CollectionExpressionVisitor buildCollectionExpressionVisitor() { |
| return new CollectionExpressionVisitor(); |
| } |
| |
| /** |
| * Creates a new {@link Resolver} for the given collection-valued path expression. |
| * |
| * @param variableName The last segment of the collection-valued path expression |
| * @return A new {@link Resolver} that can get the information for a collection-valued path |
| */ |
| protected Resolver buildCollectionValuedFieldResolver(String variableName) { |
| |
| Resolver resolver = this.resolver.getChild(variableName); |
| |
| if (resolver == null) { |
| resolver = new CollectionValuedFieldResolver(this.resolver, variableName); |
| } |
| |
| return resolver; |
| } |
| |
| /** |
| * Creates a new {@link Resolver} that will resolve the given enum literal. |
| * |
| * @param expression The {@link Expression} that represents the enum literal |
| * @param type The {@link IType} representing the {@link Enum} type |
| * @param enumLiteral The fully qualified enum constant |
| * @return The {@link Resolver} for an enum literal |
| */ |
| protected Resolver buildEnumResolver(AbstractPathExpression expression, |
| IType type, |
| String enumLiteral) { |
| |
| DeclarationResolver parent = getDeclarationResolver(expression); |
| return new EnumLiteralResolver(parent, type, enumLiteral); |
| } |
| |
| /** |
| * Creates a new {@link Resolver} that is used when nothing can be resolved. |
| * |
| * @return A "<code>null</code>" version of a {@link Resolver} |
| */ |
| protected Resolver buildNullResolver() { |
| return new NullResolver(getDeclarationResolver()); |
| } |
| |
| /** |
| * Creates a new {@link Resolver} for the given state field path expression. |
| * |
| * @param variableName The last segment of the state field path expression |
| * @return A new {@link Resolver} that can get the information for a state field path |
| */ |
| protected Resolver buildStateFieldResolver(String variableName) { |
| |
| Resolver resolver = this.resolver.getChild(variableName); |
| |
| if (resolver == null) { |
| resolver = new StateFieldResolver(this.resolver, variableName); |
| } |
| |
| return resolver; |
| } |
| |
| /** |
| * Disposes the internal data of this resolver. |
| */ |
| public void dispose() { |
| resolver = null; |
| } |
| |
| /** |
| * Casts the given {@link Expression} to a {@link CollectionExpression} if it is actually an |
| * object of that type. |
| * |
| * @param expression The {@link Expression} to cast |
| * @return The given {@link Expression} if it is a {@link CollectionExpression} or <code>null</code> |
| * if it is any other object |
| */ |
| protected CollectionExpression getCollectionExpression(Expression expression) { |
| CollectionExpressionVisitor visitor = getCollectionExpressionVisitor(); |
| try { |
| expression.accept(visitor); |
| return visitor.expression; |
| } |
| finally { |
| visitor.expression = null; |
| } |
| } |
| |
| /** |
| * Returns the visitor that collects the {@link CollectionExpression} if it's been visited. |
| * |
| * @return The {@link CollectionExpressionVisitor} |
| * @see #buildCollectionExpressionVisitor() |
| */ |
| protected CollectionExpressionVisitor getCollectionExpressionVisitor() { |
| if (collectionExpressionVisitor == null) { |
| collectionExpressionVisitor = buildCollectionExpressionVisitor(); |
| } |
| return collectionExpressionVisitor; |
| } |
| |
| /** |
| * 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 |
| */ |
| protected DeclarationResolver getDeclarationResolver() { |
| return queryContext.getDeclarationResolver(); |
| } |
| |
| /** |
| * 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 |
| */ |
| protected DeclarationResolver getDeclarationResolver(Expression expression) { |
| return queryContext.getDeclarationResolver(expression); |
| } |
| |
| /** |
| * Returns the {@link JPQLQueryContext} that contains information related to the JPQL query. |
| * |
| * @return The {@link JPQLQueryContext}, which is never <code>null</code> |
| */ |
| protected JPQLQueryContext getQueryContext() { |
| return queryContext; |
| } |
| |
| /** |
| * Returns the current {@link Resolver} used to resolve an {@link Expression}. |
| * |
| * @return The current {@link Resolver}, which should never be <code>null</code> |
| */ |
| public Resolver getResolver() { |
| return resolver; |
| } |
| |
| @Override |
| public void visit(AbsExpression expression) { |
| |
| // Visit the child expression in order to create the resolver |
| expression.getExpression().accept(this); |
| |
| // Wrap the Resolver used to determine the type of the state field |
| // path expression so we can return the actual type |
| resolver = new AbsFunctionResolver(resolver); |
| } |
| |
| @Override |
| public void visit(AbstractSchemaName expression) { |
| |
| String abstractSchemaName = expression.getText(); |
| |
| if (ExpressionTools.stringIsNotEmpty(abstractSchemaName)) { |
| |
| DeclarationResolver parent = getDeclarationResolver(expression); |
| |
| if (queryContext.isSubquery()) { |
| resolver = new SubqueryEntityResolver(parent, queryContext.getCurrentContext(), expression); |
| } |
| else { |
| resolver = new EntityResolver(parent, abstractSchemaName); |
| } |
| } |
| else { |
| resolver = buildNullResolver(); |
| } |
| } |
| |
| @Override |
| public void visit(AdditionExpression expression) { |
| visitArithmeticExpression(expression); |
| } |
| |
| @Override |
| public void visit(AllOrAnyExpression expression) { |
| expression.getExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(AndExpression expression) { |
| resolver = buildClassResolver(Boolean.class); |
| } |
| |
| @Override |
| public void visit(ArithmeticFactor expression) { |
| |
| // First traverse the expression |
| expression.getExpression().accept(this); |
| |
| // Make sure the type is a numeric type |
| DeclarationResolver parent = getDeclarationResolver(expression); |
| resolver = new NumericResolver(parent, resolver); |
| } |
| |
| @Override |
| public void visit(AvgFunction expression) { |
| resolver = buildClassResolver(Double.class); |
| } |
| |
| @Override |
| public void visit(BadExpression expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(BetweenExpression expression) { |
| resolver = buildClassResolver(Boolean.class); |
| } |
| |
| @Override |
| public void visit(CaseExpression expression) { |
| visitCollectionEquivalentExpression( |
| expression.getWhenClauses(), |
| expression.getElseExpression() |
| ); |
| } |
| |
| @Override |
| public void visit(CoalesceExpression expression) { |
| visitCollectionEquivalentExpression(expression.getExpression(), null); |
| } |
| |
| @Override |
| public void visit(CollectionExpression expression) { |
| expression.acceptChildren(this); |
| } |
| |
| @Override |
| public void visit(CollectionMemberDeclaration expression) { |
| expression.getCollectionValuedPathExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(CollectionMemberExpression expression) { |
| resolver = buildClassResolver(Boolean.class); |
| } |
| |
| @Override |
| public void visit(CollectionValuedPathExpression expression) { |
| |
| // If the path ends with '.', then the path is incomplete |
| // so we can't resolve the type |
| if (!expression.endsWithDot()) { |
| |
| // Check first to see if it's an enum type |
| String path = expression.toActualText(); |
| IType type = queryContext.getTypeRepository().getEnumType(path); |
| |
| if (type != null) { |
| resolver = buildEnumResolver(expression, type, path); |
| } |
| else { |
| expression.getIdentificationVariable().accept(this); |
| |
| for (int index = expression.hasVirtualIdentificationVariable() ? 0 : 1, count = expression.pathSize(); index < count; index++) { |
| path = expression.getPath(index); |
| if (index + 1 < count) { |
| resolver = buildStateFieldResolver(path); |
| } |
| else { |
| resolver = buildCollectionValuedFieldResolver(path); |
| } |
| } |
| } |
| } |
| else { |
| resolver = buildNullResolver(); |
| } |
| } |
| |
| @Override |
| public void visit(ComparisonExpression expression) { |
| resolver = buildClassResolver(Boolean.class); |
| } |
| |
| @Override |
| public void visit(ConcatExpression expression) { |
| resolver = buildClassResolver(String.class); |
| } |
| |
| @Override |
| public void visit(ConstructorExpression expression) { |
| |
| String className = expression.getClassName(); |
| |
| if (ExpressionTools.stringIsNotEmpty(className)) { |
| resolver = buildClassNameResolver(className); |
| } |
| else { |
| resolver = buildNullResolver(); |
| } |
| } |
| |
| @Override |
| public void visit(CountFunction expression) { |
| resolver = buildClassResolver(Long.class); |
| } |
| |
| @Override |
| public void visit(DateTime expression) { |
| |
| if (expression.isCurrentDate()) { |
| resolver = buildClassResolver(Date.class); |
| } |
| else if (expression.isCurrentTime()) { |
| resolver = buildClassResolver(Time.class); |
| } |
| else if (expression.isCurrentTimestamp()) { |
| resolver = buildClassResolver(Timestamp.class); |
| } |
| else { |
| String text = expression.getText(); |
| |
| if (text.startsWith("{d")) { |
| resolver = buildClassResolver(Date.class); |
| } |
| else if (text.startsWith("{ts")) { |
| resolver = buildClassResolver(Timestamp.class); |
| } |
| else if (text.startsWith("{t")) { |
| resolver = buildClassResolver(Time.class); |
| } |
| else { |
| resolver = buildNullResolver(); |
| } |
| } |
| } |
| |
| @Override |
| public void visit(DeleteClause expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(DeleteStatement expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(DivisionExpression expression) { |
| visitArithmeticExpression(expression); |
| } |
| |
| @Override |
| public void visit(EmptyCollectionComparisonExpression expression) { |
| resolver = buildClassResolver(Boolean.class); |
| } |
| |
| @Override |
| public void visit(EntityTypeLiteral expression) { |
| |
| String entityTypeName = expression.getEntityTypeName(); |
| |
| if (ExpressionTools.stringIsNotEmpty(entityTypeName)) { |
| DeclarationResolver parent = getDeclarationResolver(expression); |
| resolver = new EntityResolver(parent, entityTypeName); |
| } |
| else { |
| resolver = buildNullResolver(); |
| } |
| } |
| |
| @Override |
| public void visit(EntryExpression expression) { |
| resolver = buildClassResolver(Map.Entry.class); |
| } |
| |
| @Override |
| public void visit(ExistsExpression expression) { |
| resolver = buildClassResolver(Boolean.class); |
| } |
| |
| @Override |
| public void visit(FromClause expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(FunctionExpression expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(GroupByClause expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(HavingClause expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(IdentificationVariable expression) { |
| DeclarationResolver parent = getDeclarationResolver(expression); |
| resolver = parent.getResolver(expression.getVariableName()); |
| } |
| |
| @Override |
| public void visit(IdentificationVariableDeclaration expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(IndexExpression expression) { |
| resolver = buildClassResolver(Integer.class); |
| } |
| |
| @Override |
| public void visit(InExpression expression) { |
| resolver = buildClassResolver(Boolean.class); |
| } |
| |
| @Override |
| public void visit(InputParameter expression) { |
| resolver = buildClassNameResolver(IType.UNRESOLVABLE_TYPE); |
| } |
| |
| @Override |
| public void visit(Join expression) { |
| expression.getJoinAssociationPath().accept(this); |
| resolver.setNullAllowed(expression.isLeftJoin()); |
| } |
| |
| @Override |
| public void visit(JPQLExpression expression) { |
| expression.getQueryStatement().accept(this); |
| } |
| |
| @Override |
| public void visit(KeyExpression expression) { |
| |
| // Visit the identification variable in order to create the resolver |
| expression.getExpression().accept(this); |
| |
| // Wrap the Resolver used to determine the type of the identification |
| // variable so we can return the actual type |
| resolver = new KeyResolver(resolver); |
| } |
| |
| @Override |
| public void visit(KeywordExpression expression) { |
| |
| String text = expression.getText(); |
| |
| if (text == KeywordExpression.FALSE || |
| text == KeywordExpression.TRUE) { |
| |
| resolver = buildClassResolver(Boolean.class); |
| } |
| else { |
| resolver = buildClassResolver(Object.class); |
| } |
| } |
| |
| @Override |
| public void visit(LengthExpression expression) { |
| resolver = buildClassResolver(Integer.class); |
| } |
| |
| @Override |
| public void visit(LikeExpression expression) { |
| resolver = buildClassResolver(Boolean.class); |
| } |
| |
| @Override |
| public void visit(LocateExpression expression) { |
| resolver = buildClassResolver(Integer.class); |
| } |
| |
| @Override |
| public void visit(LowerExpression expression) { |
| resolver = buildClassResolver(String.class); |
| } |
| |
| @Override |
| public void visit(MaxFunction expression) { |
| // Visit the state field path expression in order to create the resolver |
| expression.getExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(MinFunction expression) { |
| // Visit the state field path expression in order to create the resolver |
| expression.getExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(ModExpression expression) { |
| resolver = buildClassResolver(Integer.class); |
| } |
| |
| @Override |
| public void visit(MultiplicationExpression expression) { |
| visitArithmeticExpression(expression); |
| } |
| |
| @Override |
| public void visit(NotExpression expression) { |
| resolver = buildClassResolver(Boolean.class); |
| } |
| |
| @Override |
| public void visit(NullComparisonExpression expression) { |
| resolver = buildClassResolver(Boolean.class); |
| } |
| |
| @Override |
| public void visit(NullExpression expression) { |
| resolver = buildClassNameResolver(IType.UNRESOLVABLE_TYPE); |
| } |
| |
| @Override |
| public void visit(NullIfExpression expression) { |
| expression.getFirstExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(NumericLiteral expression) { |
| |
| try { |
| String text = expression.getText(); |
| |
| // Long value |
| // Integer value |
| if (ExpressionTools.LONG_REGEXP .matcher(text).matches() || |
| ExpressionTools.INTEGER_REGEXP.matcher(text).matches()) { |
| |
| Long value = Long.parseLong(text); |
| |
| if (value <= Integer.MAX_VALUE) { |
| resolver = buildClassResolver(Integer.class); |
| } |
| else { |
| resolver = buildClassResolver(Long.class); |
| } |
| } |
| // Float |
| else if (ExpressionTools.FLOAT_REGEXP.matcher(text).matches()) { |
| resolver = buildClassResolver(Float.class); |
| } |
| // Decimal |
| else if (ExpressionTools.DOUBLE_REGEXP.matcher(text).matches()) { |
| resolver = buildClassResolver(Double.class); |
| } |
| } |
| catch (Exception e) { |
| resolver = buildClassResolver(Object.class); |
| } |
| } |
| |
| @Override |
| public void visit(ObjectExpression expression) { |
| expression.getExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(OnClause expression) { |
| expression.getConditionalExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(OrderByClause expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(OrderByItem expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(OrExpression expression) { |
| resolver = buildClassResolver(Boolean.class); |
| } |
| |
| @Override |
| public void visit(RangeVariableDeclaration expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(ResultVariable expression) { |
| expression.getSelectExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(SelectClause expression) { |
| |
| Expression selectExpression = expression.getSelectExpression(); |
| |
| // visit(CollectionExpression) iterates through the children but for a |
| // SELECT clause, a CollectionExpression means the result type is Object[] |
| CollectionExpression collectionExpression = getCollectionExpression(selectExpression); |
| |
| if (collectionExpression != null) { |
| resolver = buildClassResolver(Object[].class); |
| } |
| else { |
| selectExpression.accept(this); |
| } |
| } |
| |
| @Override |
| public void visit(SelectStatement expression) { |
| expression.getSelectClause().accept(this); |
| } |
| |
| @Override |
| public void visit(SimpleFromClause expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(SimpleSelectClause expression) { |
| expression.getSelectExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(SimpleSelectStatement expression) { |
| queryContext.newSubqueryContext(expression); |
| try { |
| expression.getSelectClause().accept(this); |
| } |
| finally { |
| queryContext.disposeSubqueryContext(); |
| } |
| } |
| |
| @Override |
| public void visit(SizeExpression expression) { |
| resolver = buildClassResolver(Integer.class); |
| } |
| |
| @Override |
| public void visit(SqrtExpression expression) { |
| resolver = buildClassResolver(Double.class); |
| } |
| |
| @Override |
| public void visit(StateFieldPathExpression expression) { |
| |
| // If the path ends with '.', then the path is incomplete |
| // so we can't resolve the type |
| if (!expression.endsWithDot()) { |
| |
| // Check first to see if it's an enum type |
| String path = expression.toActualText(); |
| IType type = queryContext.getTypeRepository().getEnumType(path); |
| |
| if (type != null) { |
| resolver = buildEnumResolver(expression, type, path); |
| } |
| else { |
| expression.getIdentificationVariable().accept(this); |
| |
| for (int index = expression.hasVirtualIdentificationVariable() ? 0 : 1, count = expression.pathSize(); index < count; index++) { |
| path = expression.getPath(index); |
| resolver = buildStateFieldResolver(expression.getPath(index)); |
| } |
| } |
| } |
| else { |
| resolver = buildNullResolver(); |
| } |
| } |
| |
| @Override |
| public void visit(StringLiteral expression) { |
| resolver = buildClassResolver(String.class); |
| } |
| |
| @Override |
| public void visit(SubExpression expression) { |
| expression.getExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(SubstringExpression expression) { |
| resolver = buildClassResolver(String.class); |
| } |
| |
| @Override |
| public void visit(SubtractionExpression expression) { |
| visitArithmeticExpression(expression); |
| } |
| |
| @Override |
| public void visit(SumFunction expression) { |
| |
| // Visit the state field path expression in order to create the resolver |
| expression.getExpression().accept(this); |
| |
| // Wrap the Resolver used to determine the type of the state field |
| // path expression so we can return the actual type |
| resolver = new SumFunctionResolver(resolver); |
| } |
| |
| @Override |
| public void visit(TreatExpression expression) { |
| |
| // Visit the identification variable in order to create the resolver |
| expression.getCollectionValuedPathExpression().accept(this); |
| |
| // Retrieve the entity type name |
| String entityTypeName = getQueryContext().literal( |
| expression.getEntityType(), |
| LiteralType.ENTITY_TYPE |
| ); |
| |
| // Wrap the Resolver for down casting |
| resolver = new TreatResolver(resolver, entityTypeName); |
| } |
| |
| @Override |
| public void visit(TrimExpression expression) { |
| resolver = buildClassResolver(String.class); |
| } |
| |
| @Override |
| public void visit(TypeExpression expression) { |
| expression.getExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(UnknownExpression expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(UpdateClause expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(UpdateItem expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(UpdateStatement expression) { |
| resolver = buildClassResolver(Object.class); |
| } |
| |
| @Override |
| public void visit(UpperExpression expression) { |
| resolver = buildClassResolver(String.class); |
| } |
| |
| @Override |
| public void visit(ValueExpression expression) { |
| |
| // Visit the identification variable in order to create the resolver |
| expression.getExpression().accept(this); |
| |
| // Wrap the Resolver used to determine the type of the identification |
| // variable so we can return the actual type |
| resolver = new ValueResolver(resolver); |
| } |
| |
| @Override |
| public void visit(WhenClause expression) { |
| expression.getThenExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(WhereClause expression) { |
| expression.getConditionalExpression().accept(this); |
| } |
| |
| /** |
| * Visits the given {@link ArithmeticExpression} and create the appropriate {@link Resolver}. |
| * |
| * @param expression The {@link ArithmeticExpression} to visit |
| */ |
| protected void visitArithmeticExpression(ArithmeticExpression expression) { |
| |
| List<Resolver> resolvers = new ArrayList<Resolver>(2); |
| |
| // Visit the first expression |
| expression.getLeftExpression().accept(this); |
| resolvers.add(resolver); |
| |
| // Visit the second expression |
| expression.getRightExpression().accept(this); |
| resolvers.add(resolver); |
| |
| // This will resolve the correct numeric type |
| DeclarationResolver parent = getDeclarationResolver(expression); |
| resolver = new NumericResolver(parent, resolvers); |
| } |
| |
| /** |
| * Visits the given {@link Expression} and creates a {@link Resolver} that will check the type |
| * for each of its children. If the type is the same, then it's the {@link Expression}'s type; |
| * otherwise the type will be {@link Object}. |
| * |
| * @param expression The {@link Expression} to calculate the type of its children |
| * @param extraExpression This {@link Expression} will be resolved, if it's not <code>null</code> |
| * and its type will be added to the collection of types |
| */ |
| protected void visitCollectionEquivalentExpression(Expression expression, |
| Expression extraExpression) { |
| |
| List<Resolver> resolvers = new ArrayList<Resolver>(); |
| CollectionExpression collectionExpression = getCollectionExpression(expression); |
| |
| // Gather the resolver for all children |
| if (collectionExpression != null) { |
| for (Expression child : collectionExpression.children()) { |
| child.accept(this); |
| resolvers.add(resolver); |
| } |
| } |
| // Otherwise visit the actual expression |
| else { |
| expression.accept(this); |
| resolvers.add(resolver); |
| } |
| |
| // Add the resolver for the other expression |
| if (extraExpression != null) { |
| extraExpression.accept(this); |
| resolvers.add(resolver); |
| } |
| |
| DeclarationResolver parent = getDeclarationResolver(expression); |
| resolver = new CollectionEquivalentResolver(parent, resolvers); |
| } |
| |
| /** |
| * This visitor is used to check if the expression visited is a {@link CollectionExpression}. |
| */ |
| protected static final class CollectionExpressionVisitor extends AbstractExpressionVisitor { |
| |
| /** |
| * The {@link CollectionExpression} that was visited, otherwise <code>null</code>. |
| */ |
| protected CollectionExpression expression; |
| |
| /** |
| * Creates a new <code>CollectionExpressionVisitor</code>. |
| */ |
| public CollectionExpressionVisitor() { |
| super(); |
| } |
| |
| @Override |
| public void visit(CollectionExpression expression) { |
| this.expression = expression; |
| } |
| } |
| } |