/******************************************************************************* * Copyright (c) 2011, 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.internal.jpa.jpql; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import org.eclipse.persistence.descriptors.ClassDescriptor; import org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor; import org.eclipse.persistence.internal.descriptors.MethodAttributeAccessor; import org.eclipse.persistence.internal.queries.ContainerPolicy; import org.eclipse.persistence.internal.queries.MappedKeyMapContainerPolicy; import org.eclipse.persistence.jpa.jpql.ExpressionTools; import org.eclipse.persistence.jpa.jpql.parser.AbsExpression; import org.eclipse.persistence.jpa.jpql.parser.AbstractEclipseLinkExpressionVisitor; 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.AsOfClause; 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.CastExpression; 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.ConnectByClause; import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; import org.eclipse.persistence.jpa.jpql.parser.CountFunction; import org.eclipse.persistence.jpa.jpql.parser.DatabaseType; 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.EclipseLinkExpressionVisitor; 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.ExtractExpression; 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.HierarchicalQueryClause; 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.OrderSiblingsByClause; import org.eclipse.persistence.jpa.jpql.parser.RangeVariableDeclaration; import org.eclipse.persistence.jpa.jpql.parser.RegexpExpression; 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.StartWithClause; 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.TableExpression; import org.eclipse.persistence.jpa.jpql.parser.TableVariableDeclaration; 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.UnionClause; 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.mappings.AggregateMapping; import org.eclipse.persistence.mappings.AttributeAccessor; import org.eclipse.persistence.mappings.DatabaseMapping; import org.eclipse.persistence.mappings.DirectMapMapping; import org.eclipse.persistence.mappings.querykeys.DirectQueryKey; import org.eclipse.persistence.mappings.querykeys.ForeignReferenceQueryKey; import org.eclipse.persistence.mappings.querykeys.QueryKey; /** * This visitor resolves the type of any given {@link Expression}. * * @version 2.6 * @since 2.4 * @author Pascal Filion */ @SuppressWarnings("nls") final class TypeResolver implements EclipseLinkExpressionVisitor { /** * This visitor is responsible to retrieve the {@link CollectionExpression} if it is visited. */ private CollectionExpressionVisitor collectionExpressionVisitor; /** * This {@link Comparator} compares numeric types and sorts them based on precedence. */ private Comparator<Class<?>> numericTypeComparator; /** * This visitor resolves a path expression by retrieving the mapping and descriptor of the last segment. */ private PathResolver pathResolver; /** * The context used to query information about the application metadata and cached information. */ private final JPQLQueryContext queryContext; /** * The well defined type, which does not have to be calculated. */ private Class<?> type; /** * A constant representing an unresolvable type. */ private static final Class<?> UNRESOLVABLE_TYPE = TypeResolver.class; /** * Creates a new <code>TypeResolver</code>. * * @param queryContext The context used to query information about the application metadata and * cached information */ TypeResolver(JPQLQueryContext queryContext) { super(); this.queryContext = queryContext; } /** * 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 */ @SuppressWarnings("null") Class<?> calculateMappingType(DatabaseMapping mapping) { // For aggregate mappings (@Embedded and @EmbeddedId), we need to use the descriptor // because its mappings have to be retrieve from this one and not from the descriptor // returned when querying it with a Java type if (mapping.isAggregateMapping()) { ClassDescriptor descriptor = ((AggregateMapping) mapping).getReferenceDescriptor(); if (descriptor != null) { return descriptor.getJavaClass(); } } // Relationship mapping if (mapping.isForeignReferenceMapping()) { ClassDescriptor descriptor = mapping.getReferenceDescriptor(); if (descriptor != null) { return descriptor.getJavaClass(); } } // Collection mapping else if (mapping.isCollectionMapping()) { return mapping.getContainerPolicy().getContainerClass(); } // Property mapping AttributeAccessor accessor = mapping.getAttributeAccessor(); // Attribute if (accessor.isInstanceVariableAttributeAccessor()) { InstanceVariableAttributeAccessor attributeAccessor = (InstanceVariableAttributeAccessor) accessor; Field field = attributeAccessor.getAttributeField(); if (field == null) { try { field = mapping.getDescriptor().getJavaClass().getDeclaredField(attributeAccessor.getAttributeName()); } catch (Exception e) {} } return field.getType(); } // Property if (accessor.isMethodAttributeAccessor()) { MethodAttributeAccessor methodAccessor = (MethodAttributeAccessor) accessor; Method method = methodAccessor.getGetMethod(); if (method == null) { try { method = mapping.getDescriptor().getJavaClass().getDeclaredMethod(methodAccessor.getGetMethodName()); } catch (Exception e) {} } return method.getReturnType(); } // Anything else return accessor.getAttributeClass(); } /** * 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) { // ForeignReferenceQueryKey if (queryKey.isForeignReferenceQueryKey()) { ForeignReferenceQueryKey foreignReferenceQueryKey = (ForeignReferenceQueryKey) queryKey; return foreignReferenceQueryKey.getReferenceClass(); } // DirectQueryKey DirectQueryKey key = (DirectQueryKey) queryKey; Class<?> type = key.getField().getType(); return (type != null) ? type : Object.class; } /** * Returns the visitor that collects the {@link CollectionExpression} if it's been visited. * * @return The {@link CollectionExpressionVisitor} */ protected CollectionExpressionVisitor collectionExpressionVisitor() { if (collectionExpressionVisitor == null) { collectionExpressionVisitor = new CollectionExpressionVisitor(); } return collectionExpressionVisitor; } Class<?> compareCollectionEquivalentTypes(List<Class<?>> types) { Class<?> localType = null; for (Class<?> anotherType : types) { if (anotherType == UNRESOLVABLE_TYPE) { continue; } if (localType == null) { localType = anotherType; } // Two types are not the same, then the type is Object else if (localType != anotherType) { return Object.class; } } if (localType == null) { localType = UNRESOLVABLE_TYPE; } return localType; } Class<?> convertSumFunctionType(Class<?> type) { // Integral types if ((type == Integer.TYPE) || (type == Integer.class) || (type == Long.TYPE) || (type == Long.class) || (type == Byte.TYPE) || (type == Byte.class) || (type == Short.TYPE) || (type == Short.class) || (type == Character.TYPE) || (type == Character.class)) { type = Long.class; } // Floating types else if ((type == Float.TYPE) || (type == Float.class) || (type == Double.TYPE) || (type == Double.class)) { type = Double.class; } // Anything else, use Object else if ((type != BigDecimal.class) && (type != BigInteger.class)) { type = Object.class; } return type; } /** * 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 */ private CollectionExpression getCollectionExpression(Expression expression) { CollectionExpressionVisitor visitor = collectionExpressionVisitor(); try { expression.accept(visitor); return visitor.expression; } finally { visitor.expression = null; } } private boolean isNumericType() { return type == Integer.TYPE || type == Integer.class || type == Long.TYPE || type == Long.class || type == Float.TYPE || type == Float.class || type == Double.TYPE || type == Double.class || type == BigInteger.class || type == BigDecimal.class; } private PathResolver pathResolver() { if (pathResolver == null) { pathResolver = new PathResolver(); } return pathResolver; } /** * Returns the type of the given {@link Expression}. * * @param expression The {@link Expression} to resolve its type * @return Either the closest type or {@link Object} if it could not be determined */ Class<?> resolve(Expression expression) { Class<?> oldType = type; try { expression.accept(this); return (type == UNRESOLVABLE_TYPE) ? Object.class : type; } finally { type = oldType; } } ClassDescriptor resolveDescriptor(Expression expression) { PathResolver resolver = pathResolver(); DatabaseMapping oldMapping = resolver.mapping; ClassDescriptor oldDescriptor = resolver.descriptor; try { resolver.mapping = null; resolver.descriptor = null; expression.accept(resolver); return resolver.descriptor; } finally { resolver.mapping = oldMapping; resolver.descriptor = oldDescriptor; } } DatabaseMapping resolveMapping(Expression expression) { PathResolver resolver = pathResolver(); QueryKey oldQueryKey = resolver.queryKey; DatabaseMapping oldMapping = resolver.mapping; ClassDescriptor oldDescriptor = resolver.descriptor; try { resolver.mapping = null; resolver.descriptor = null; resolver.queryKey = null; expression.accept(resolver); return resolver.mapping; } finally { resolver.mapping = oldMapping; resolver.queryKey = oldQueryKey; resolver.descriptor = oldDescriptor; } } /** * 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(Expression expression) { PathResolver resolver = pathResolver(); QueryKey oldQueryKey = resolver.queryKey; DatabaseMapping oldMapping = resolver.mapping; ClassDescriptor oldDescriptor = resolver.descriptor; try { resolver.mapping = null; resolver.descriptor = null; resolver.queryKey = null; expression.accept(resolver); return (resolver.mapping != null) ? resolver.mapping : resolver.queryKey; } finally { resolver.mapping = oldMapping; resolver.queryKey = oldQueryKey; resolver.descriptor = oldDescriptor; } } private Class<?> resolveMappingType(AbstractPathExpression expression) { PathResolver resolver = pathResolver(); QueryKey oldQueryKey = resolver.queryKey; DatabaseMapping oldMapping = resolver.mapping; ClassDescriptor oldDescriptor = resolver.descriptor; try { resolver.mapping = null; resolver.descriptor = null; resolver.queryKey = null; expression.accept(resolver); if (resolver.mapping != null) { return calculateMappingType(resolver.mapping); } else if (resolver.queryKey != null) { return calculateQueryKeyType(resolver.queryKey); } else { return queryContext.getEnumType(expression.toParsedText()); } } finally { resolver.mapping = oldMapping; resolver.queryKey = oldQueryKey; resolver.descriptor = oldDescriptor; } } QueryKey resolveQueryKey(org.eclipse.persistence.jpa.jpql.parser.Expression expression) { PathResolver resolver = pathResolver(); QueryKey oldQueryKey = resolver.queryKey; DatabaseMapping oldMapping = resolver.mapping; ClassDescriptor oldDescriptor = resolver.descriptor; try { resolver.mapping = null; resolver.descriptor = null; resolver.queryKey = null; expression.accept(resolver); return resolver.queryKey; } finally { resolver.mapping = oldMapping; resolver.queryKey = oldQueryKey; resolver.descriptor = oldDescriptor; } } /** * {@inheritDoc} */ @Override public void visit(AbsExpression expression) { // Visit the child expression in order to create the resolver expression.getExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(AbstractSchemaName expression) { ClassDescriptor descriptor = queryContext.getDescriptor(expression.getText()); type = descriptor.getJavaClass(); } /** * {@inheritDoc} */ @Override public void visit(AdditionExpression expression) { visitArithmeticExpression(expression); } /** * {@inheritDoc} */ @Override public void visit(AllOrAnyExpression expression) { expression.getExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(AndExpression expression) { type = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(ArithmeticFactor expression) { // First traverse the expression expression.getExpression().accept(this); // Make sure the type is a numeric type if (!isNumericType()) { type = Object.class; } } /** * {@inheritDoc} */ @Override public void visit(AsOfClause expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(AvgFunction expression) { type = Double.class; } /** * {@inheritDoc} */ @Override public void visit(BadExpression expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(BetweenExpression expression) { type = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(CaseExpression expression) { visitCollectionEquivalentExpression( expression.getWhenClauses(), expression.getElseExpression() ); } /** * {@inheritDoc} */ @Override public void visit(CastExpression expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(CoalesceExpression expression) { visitCollectionEquivalentExpression(expression.getExpression(), null); } /** * {@inheritDoc} */ @Override public void visit(CollectionExpression expression) { expression.acceptChildren(this); } /** * {@inheritDoc} */ @Override public void visit(CollectionMemberDeclaration expression) { expression.getCollectionValuedPathExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(CollectionMemberExpression expression) { type = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(CollectionValuedPathExpression expression) { type = resolveMappingType(expression); } /** * {@inheritDoc} */ @Override public void visit(ComparisonExpression expression) { type = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(ConcatExpression expression) { type = String.class; } /** * {@inheritDoc} */ @Override public void visit(ConnectByClause expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(ConstructorExpression expression) { type = queryContext.getType(expression.getClassName()); } /** * {@inheritDoc} */ @Override public void visit(CountFunction expression) { type = Long.class; } /** * {@inheritDoc} */ @Override public void visit(DatabaseType expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(DateTime expression) { if (expression.isCurrentDate()) { type = Date.class; } else if (expression.isCurrentTime()) { type = Time.class; } else if (expression.isCurrentTimestamp()) { type = Timestamp.class; } else { String text = expression.getText(); if (text.startsWith("{d")) { type = Date.class; } else if (text.startsWith("{ts")) { type = Timestamp.class; } else if (text.startsWith("{t")) { type = Time.class; } else { type = Object.class; } } } /** * {@inheritDoc} */ @Override public void visit(DeleteClause expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(DeleteStatement expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(DivisionExpression expression) { visitArithmeticExpression(expression); } /** * {@inheritDoc} */ @Override public void visit(EmptyCollectionComparisonExpression expression) { type = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(EntityTypeLiteral expression) { String entityTypeName = expression.getEntityTypeName(); ClassDescriptor descriptor = queryContext.getDescriptor(entityTypeName); type = descriptor.getJavaClass(); } /** * {@inheritDoc} */ @Override public void visit(EntryExpression expression) { type = Map.Entry.class; } /** * {@inheritDoc} */ @Override public void visit(ExistsExpression expression) { type = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(ExtractExpression expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(FromClause expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(FunctionExpression expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(GroupByClause expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(HavingClause expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(HierarchicalQueryClause expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(IdentificationVariable expression) { PathResolver resolver = pathResolver(); DatabaseMapping oldMapping = resolver.mapping; ClassDescriptor oldDescriptor = resolver.descriptor; try { resolver.mapping = null; resolver.descriptor = null; expression.accept(resolver); if (resolver.mapping != null) { type = calculateMappingType(resolver.mapping); } else if (resolver.descriptor != null) { type = resolver.descriptor.getJavaClass(); } else { type = Object.class; } } finally { resolver.mapping = oldMapping; resolver.descriptor = oldDescriptor; } } /** * {@inheritDoc} */ @Override public void visit(IdentificationVariableDeclaration expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(IndexExpression expression) { type = Integer.class; } /** * {@inheritDoc} */ @Override public void visit(InExpression expression) { type = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(InputParameter expression) { type = UNRESOLVABLE_TYPE; } /** * {@inheritDoc} */ @Override public void visit(Join expression) { expression.getJoinAssociationPath().accept(this); } /** * {@inheritDoc} */ @Override public void visit(JPQLExpression expression) { expression.getQueryStatement().accept(this); } /** * {@inheritDoc} */ @Override public void visit(KeyExpression expression) { IdentificationVariable identificationVariable = (IdentificationVariable) expression.getExpression(); Declaration declaration = queryContext.findDeclaration(identificationVariable.getVariableName()); DatabaseMapping mapping = declaration.getMapping(); MappedKeyMapContainerPolicy mapContainerPolicy = (MappedKeyMapContainerPolicy) mapping.getContainerPolicy(); type = (Class<?>) mapContainerPolicy.getKeyType(); } /** * {@inheritDoc} */ @Override public void visit(KeywordExpression expression) { String text = expression.getText(); if (text == KeywordExpression.FALSE || text == KeywordExpression.TRUE) { type = Boolean.class; } else { type = Object.class; } } /** * {@inheritDoc} */ @Override public void visit(LengthExpression expression) { type = Integer.class; } /** * {@inheritDoc} */ @Override public void visit(LikeExpression expression) { type = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(LocateExpression expression) { type = Integer.class; } /** * {@inheritDoc} */ @Override public void visit(LowerExpression expression) { type = String.class; } /** * {@inheritDoc} */ @Override public void visit(MaxFunction 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 if (!isNumericType()) { type = Object.class; } } /** * {@inheritDoc} */ @Override public void visit(MinFunction 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 if (!isNumericType()) { type = Object.class; } } /** * {@inheritDoc} */ @Override public void visit(ModExpression expression) { type = Integer.class; } /** * {@inheritDoc} */ @Override public void visit(MultiplicationExpression expression) { visitArithmeticExpression(expression); } /** * {@inheritDoc} */ @Override public void visit(NotExpression expression) { type = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(NullComparisonExpression expression) { type = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(NullExpression expression) { type = UNRESOLVABLE_TYPE; } /** * {@inheritDoc} */ @Override public void visit(NullIfExpression expression) { expression.getFirstExpression().accept(this); } /** * {@inheritDoc} */ @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()) { // Special case for a long number, Long.parseLong() does not handle 'l|L' if (text.endsWith("L") || text.endsWith("l")) { type = Long.class; } else { Long value = Long.parseLong(text); if (value <= Integer.MAX_VALUE) { type = Integer.class; } else { type = Long.class; } } } // Float else if (ExpressionTools.FLOAT_REGEXP.matcher(text).matches()) { type = Float.class; } // Decimal else if (ExpressionTools.DOUBLE_REGEXP.matcher(text).matches()) { type = Double.class; } } catch (Exception e) { type = Object.class; } } /** * {@inheritDoc} */ @Override public void visit(ObjectExpression expression) { expression.getExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(OnClause expression) { expression.getConditionalExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(OrderByClause expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(OrderByItem expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(OrderSiblingsByClause expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(OrExpression expression) { type = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(RangeVariableDeclaration expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(RegexpExpression expression) { type = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(ResultVariable expression) { expression.getSelectExpression().accept(this); } /** * {@inheritDoc} */ @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) { type = Object[].class; } else { selectExpression.accept(this); } } /** * {@inheritDoc} */ @Override public void visit(SelectStatement expression) { expression.getSelectClause().accept(this); } /** * {@inheritDoc} */ @Override public void visit(SimpleFromClause expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(SimpleSelectClause expression) { expression.getSelectExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(SimpleSelectStatement expression) { queryContext.newSubQueryContext(expression, null); try { expression.getSelectClause().accept(this); } finally { queryContext.disposeSubqueryContext(); } } /** * {@inheritDoc} */ @Override public void visit(SizeExpression expression) { type = Integer.class; } /** * {@inheritDoc} */ @Override public void visit(SqrtExpression expression) { type = Double.class; } /** * {@inheritDoc} */ @Override public void visit(StartWithClause expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(StateFieldPathExpression expression) { type = resolveMappingType(expression); } /** * {@inheritDoc} */ @Override public void visit(StringLiteral expression) { type = String.class; } /** * {@inheritDoc} */ @Override public void visit(SubExpression expression) { expression.getExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(SubstringExpression expression) { type = String.class; } /** * {@inheritDoc} */ @Override public void visit(SubtractionExpression expression) { visitArithmeticExpression(expression); } /** * {@inheritDoc} */ @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 type = convertSumFunctionType(type); } /** * {@inheritDoc} */ @Override public void visit(TableExpression expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(TableVariableDeclaration expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(TreatExpression expression) { expression.getEntityType().accept(this); } /** * {@inheritDoc} */ @Override public void visit(TrimExpression expression) { type = String.class; } /** * {@inheritDoc} */ @Override public void visit(TypeExpression expression) { expression.getExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(UnionClause expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(UnknownExpression expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(UpdateClause expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(UpdateItem expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(UpdateStatement expression) { type = Object.class; } /** * {@inheritDoc} */ @Override public void visit(UpperExpression expression) { type = String.class; } /** * {@inheritDoc} */ @Override public void visit(ValueExpression expression) { IdentificationVariable identificationVariable = (IdentificationVariable) expression.getExpression(); Declaration declaration = queryContext.findDeclaration(identificationVariable.getVariableName()); DatabaseMapping mapping = declaration.getMapping(); if (mapping.isDirectMapMapping()) { DirectMapMapping mapMapping = (DirectMapMapping) mapping; type = mapMapping.getValueClass(); if (type == null) { type = mapMapping.getDirectField().getType(); } } else { type = calculateMappingType(declaration.getMapping()); } } /** * {@inheritDoc} */ @Override public void visit(WhenClause expression) { expression.getThenExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(WhereClause expression) { expression.getConditionalExpression().accept(this); } /** * Visits the given {@link ArithmeticExpression} and creates the appropriate {@link org.eclipse. * persistence.expressions.Expression Expression}. * * @param expression The {@link ArithmeticExpression} to visit */ private void visitArithmeticExpression(ArithmeticExpression expression) { List<Class<?>> types = new ArrayList<Class<?>>(2); // Visit the first expression expression.getLeftExpression().accept(this); if (isNumericType()) { types.add(type); } // Visit the second expression expression.getRightExpression().accept(this); if (isNumericType()) { types.add(type); } if (types.size() == 2) { Collections.sort(types, NumericTypeComparator.instance()); type = types.get(0); } else { type = Object.class; } } /** * Visits the given {@link Expression} and creates the appropriate {@link org.eclipse.persistence. * expressions.Expression Expression} 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 */ private void visitCollectionEquivalentExpression(Expression expression, Expression extraExpression) { List<Class<?>> types = new ArrayList<Class<?>>(); CollectionExpression collectionExpression = getCollectionExpression(expression); // Gather the resolver for all children if (collectionExpression != null) { for (Expression child : collectionExpression.children()) { child.accept(this); types.add(type); } } // Otherwise visit the actual expression else { expression.accept(this); types.add(type); } // Add the resolver for the other expression if (extraExpression != null) { extraExpression.accept(this); types.add(type); } // Now compare the types type = compareCollectionEquivalentTypes(types); } /** * This visitor is used to check if the expression visited is a {@link CollectionExpression}. */ private static 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; } } private class PathResolver extends AbstractEclipseLinkExpressionVisitor { ClassDescriptor descriptor; DatabaseMapping mapping; QueryKey queryKey; /** * {@inheritDoc} */ @Override public void visit(AbstractSchemaName expression) { descriptor = queryContext.getDescriptor(expression.getText()); } /** * {@link InputParameter} */ @Override public void visit(CollectionValuedPathExpression expression) { visitPathExpression(expression); } /** * {@inheritDoc} */ @Override public void visit(EntityTypeLiteral expression) { descriptor = queryContext.getDescriptor(expression.getEntityTypeName()); } /** * {@link InputParameter} */ @Override public void visit(IdentificationVariable expression) { // Check to see if the identification variable is "virtual" and internally // changed to a state field path expression. If so, it means it's an unqualified // path found in an UPDATE or DELETE query StateFieldPathExpression pathExpression = expression.isVirtual() ? expression.getStateFieldPathExpression() : null; if (pathExpression != null) { pathExpression.accept(this); } else { Declaration declaration = queryContext.findDeclaration(expression.getVariableName()); // A null declaration Expression would mean it's the first package of an enum type if (declaration != null) { descriptor = declaration.getDescriptor(); } } } /** * {@link InputParameter} */ @Override public void visit(Join expression) { expression.getJoinAssociationPath().accept(this); } /** * {@link InputParameter} */ @Override public void visit(KeyExpression expression) { IdentificationVariable identificationVariable = (IdentificationVariable) expression.getExpression(); Declaration declaration = queryContext.getDeclaration(identificationVariable.getVariableName()); DatabaseMapping mapping = declaration.getMapping(); ContainerPolicy containerPolicy = mapping.getContainerPolicy(); MappedKeyMapContainerPolicy mapPolicy = (MappedKeyMapContainerPolicy) containerPolicy; descriptor = mapPolicy.getKeyMapping().getReferenceDescriptor(); } /** * {@inheritDoc} */ @Override public void visit(RangeVariableDeclaration expression) { expression.getIdentificationVariable().accept(this); } @Override public void visit(StateFieldPathExpression expression) { visitPathExpression(expression); } /** * {@link InputParameter} */ @Override public void visit(TreatExpression expression) { expression.getEntityType().accept(this); } /** * {@link InputParameter} */ @Override public void visit(ValueExpression expression) { IdentificationVariable identificationVariable = (IdentificationVariable) expression.getExpression(); Declaration declaration = queryContext.getDeclaration(identificationVariable.getVariableName()); descriptor = declaration.getDescriptor(); } private void visitPathExpression(AbstractPathExpression expression) { expression.getIdentificationVariable().accept(this); if (descriptor == null) { return; } // Now traverse the rest of the path for (int index = expression.hasVirtualIdentificationVariable() ? 0 : 1, count = expression.pathSize(); index < count; index++) { String path = expression.getPath(index); mapping = descriptor.getObjectBuilder().getMappingForAttributeName(path); if (mapping == null) { queryKey = descriptor.getQueryKeyNamed(path); if ((queryKey != null) && queryKey.isForeignReferenceQueryKey()) { ForeignReferenceQueryKey referenceQueryKey = (ForeignReferenceQueryKey) queryKey; descriptor = queryContext.getDescriptor(referenceQueryKey.getReferenceClass()); } else { if (index + 1 < count) { mapping = null; } descriptor = null; } } // A collection mapping cannot be used in a path (if it's not the last path) else if (mapping.isCollectionMapping() && (index + 1 < count)) { mapping = null; descriptor = null; } else { descriptor = mapping.getReferenceDescriptor(); } if (descriptor == null) { if (index + 1 < count) { mapping = null; } break; } } } } }