/******************************************************************************* * 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.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; } /** * {@inheritDoc} */ 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); } /** * {@inheritDoc} */ 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(); } } /** * {@inheritDoc} */ public void visit(AdditionExpression expression) { visitArithmeticExpression(expression); } /** * {@inheritDoc} */ public void visit(AllOrAnyExpression expression) { expression.getExpression().accept(this); } /** * {@inheritDoc} */ public void visit(AndExpression expression) { resolver = buildClassResolver(Boolean.class); } /** * {@inheritDoc} */ 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); } /** * {@inheritDoc} */ public void visit(AvgFunction expression) { resolver = buildClassResolver(Double.class); } /** * {@inheritDoc} */ public void visit(BadExpression expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(BetweenExpression expression) { resolver = buildClassResolver(Boolean.class); } /** * {@inheritDoc} */ public void visit(CaseExpression expression) { visitCollectionEquivalentExpression( expression.getWhenClauses(), expression.getElseExpression() ); } /** * {@inheritDoc} */ public void visit(CoalesceExpression expression) { visitCollectionEquivalentExpression(expression.getExpression(), null); } /** * {@inheritDoc} */ public void visit(CollectionExpression expression) { expression.acceptChildren(this); } /** * {@inheritDoc} */ public void visit(CollectionMemberDeclaration expression) { expression.getCollectionValuedPathExpression().accept(this); } /** * {@inheritDoc} */ public void visit(CollectionMemberExpression expression) { resolver = buildClassResolver(Boolean.class); } /** * {@inheritDoc} */ 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(); } } /** * {@inheritDoc} */ public void visit(ComparisonExpression expression) { resolver = buildClassResolver(Boolean.class); } /** * {@inheritDoc} */ public void visit(ConcatExpression expression) { resolver = buildClassResolver(String.class); } /** * {@inheritDoc} */ public void visit(ConstructorExpression expression) { String className = expression.getClassName(); if (ExpressionTools.stringIsNotEmpty(className)) { resolver = buildClassNameResolver(className); } else { resolver = buildNullResolver(); } } /** * {@inheritDoc} */ public void visit(CountFunction expression) { resolver = buildClassResolver(Long.class); } /** * {@inheritDoc} */ 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(); } } } /** * {@inheritDoc} */ public void visit(DeleteClause expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(DeleteStatement expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(DivisionExpression expression) { visitArithmeticExpression(expression); } /** * {@inheritDoc} */ public void visit(EmptyCollectionComparisonExpression expression) { resolver = buildClassResolver(Boolean.class); } /** * {@inheritDoc} */ 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(); } } /** * {@inheritDoc} */ public void visit(EntryExpression expression) { resolver = buildClassResolver(Map.Entry.class); } /** * {@inheritDoc} */ public void visit(ExistsExpression expression) { resolver = buildClassResolver(Boolean.class); } /** * {@inheritDoc} */ public void visit(FromClause expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(FunctionExpression expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(GroupByClause expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(HavingClause expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(IdentificationVariable expression) { DeclarationResolver parent = getDeclarationResolver(expression); resolver = parent.getResolver(expression.getVariableName()); } /** * {@inheritDoc} */ public void visit(IdentificationVariableDeclaration expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(IndexExpression expression) { resolver = buildClassResolver(Integer.class); } /** * {@inheritDoc} */ public void visit(InExpression expression) { resolver = buildClassResolver(Boolean.class); } /** * {@inheritDoc} */ public void visit(InputParameter expression) { resolver = buildClassNameResolver(IType.UNRESOLVABLE_TYPE); } /** * {@inheritDoc} */ public void visit(Join expression) { expression.getJoinAssociationPath().accept(this); resolver.setNullAllowed(expression.isLeftJoin()); } /** * {@inheritDoc} */ public void visit(JPQLExpression expression) { expression.getQueryStatement().accept(this); } /** * {@inheritDoc} */ 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); } /** * {@inheritDoc} */ 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); } } /** * {@inheritDoc} */ public void visit(LengthExpression expression) { resolver = buildClassResolver(Integer.class); } /** * {@inheritDoc} */ public void visit(LikeExpression expression) { resolver = buildClassResolver(Boolean.class); } /** * {@inheritDoc} */ public void visit(LocateExpression expression) { resolver = buildClassResolver(Integer.class); } /** * {@inheritDoc} */ public void visit(LowerExpression expression) { resolver = buildClassResolver(String.class); } /** * {@inheritDoc} */ public void visit(MaxFunction expression) { // Visit the state field path expression in order to create the resolver expression.getExpression().accept(this); } /** * {@inheritDoc} */ public void visit(MinFunction expression) { // Visit the state field path expression in order to create the resolver expression.getExpression().accept(this); } /** * {@inheritDoc} */ public void visit(ModExpression expression) { resolver = buildClassResolver(Integer.class); } /** * {@inheritDoc} */ public void visit(MultiplicationExpression expression) { visitArithmeticExpression(expression); } /** * {@inheritDoc} */ public void visit(NotExpression expression) { resolver = buildClassResolver(Boolean.class); } /** * {@inheritDoc} */ public void visit(NullComparisonExpression expression) { resolver = buildClassResolver(Boolean.class); } /** * {@inheritDoc} */ public void visit(NullExpression expression) { resolver = buildClassNameResolver(IType.UNRESOLVABLE_TYPE); } /** * {@inheritDoc} */ public void visit(NullIfExpression expression) { expression.getFirstExpression().accept(this); } /** * {@inheritDoc} */ 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); } } /** * {@inheritDoc} */ public void visit(ObjectExpression expression) { expression.getExpression().accept(this); } /** * {@inheritDoc} */ public void visit(OnClause expression) { expression.getConditionalExpression().accept(this); } /** * {@inheritDoc} */ public void visit(OrderByClause expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(OrderByItem expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(OrExpression expression) { resolver = buildClassResolver(Boolean.class); } /** * {@inheritDoc} */ public void visit(RangeVariableDeclaration expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(ResultVariable expression) { expression.getSelectExpression().accept(this); } /** * {@inheritDoc} */ 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); } } /** * {@inheritDoc} */ public void visit(SelectStatement expression) { expression.getSelectClause().accept(this); } /** * {@inheritDoc} */ public void visit(SimpleFromClause expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(SimpleSelectClause expression) { expression.getSelectExpression().accept(this); } /** * {@inheritDoc} */ public void visit(SimpleSelectStatement expression) { queryContext.newSubqueryContext(expression); try { expression.getSelectClause().accept(this); } finally { queryContext.disposeSubqueryContext(); } } /** * {@inheritDoc} */ public void visit(SizeExpression expression) { resolver = buildClassResolver(Integer.class); } /** * {@inheritDoc} */ public void visit(SqrtExpression expression) { resolver = buildClassResolver(Double.class); } /** * {@inheritDoc} */ 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(); } } /** * {@inheritDoc} */ public void visit(StringLiteral expression) { resolver = buildClassResolver(String.class); } /** * {@inheritDoc} */ public void visit(SubExpression expression) { expression.getExpression().accept(this); } /** * {@inheritDoc} */ public void visit(SubstringExpression expression) { resolver = buildClassResolver(String.class); } /** * {@inheritDoc} */ public void visit(SubtractionExpression expression) { visitArithmeticExpression(expression); } /** * {@inheritDoc} */ 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); } /** * {@inheritDoc} */ 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); } /** * {@inheritDoc} */ public void visit(TrimExpression expression) { resolver = buildClassResolver(String.class); } /** * {@inheritDoc} */ public void visit(TypeExpression expression) { expression.getExpression().accept(this); } /** * {@inheritDoc} */ public void visit(UnknownExpression expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(UpdateClause expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(UpdateItem expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(UpdateStatement expression) { resolver = buildClassResolver(Object.class); } /** * {@inheritDoc} */ public void visit(UpperExpression expression) { resolver = buildClassResolver(String.class); } /** * {@inheritDoc} */ 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); } /** * {@inheritDoc} */ public void visit(WhenClause expression) { expression.getThenExpression().accept(this); } /** * {@inheritDoc} */ 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(); } /** * {@inheritDoc} */ @Override public void visit(CollectionExpression expression) { this.expression = expression; } } }