/******************************************************************************* * 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.internal.jpa.jpql; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.persistence.descriptors.ClassDescriptor; import org.eclipse.persistence.expressions.Expression; import org.eclipse.persistence.expressions.ExpressionBuilder; import org.eclipse.persistence.expressions.ExpressionMath; import org.eclipse.persistence.internal.expressions.ConstantExpression; import org.eclipse.persistence.internal.expressions.DateConstantExpression; import org.eclipse.persistence.internal.expressions.MapEntryExpression; import org.eclipse.persistence.internal.expressions.ParameterExpression; import org.eclipse.persistence.internal.queries.ReportItem; import org.eclipse.persistence.jpa.jpql.ExpressionTools; import org.eclipse.persistence.jpa.jpql.JPQLQueryDeclaration.Type; import org.eclipse.persistence.jpa.jpql.LiteralType; 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.AnonymousExpressionVisitor; 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.EclipseLinkAnonymousExpressionVisitor; 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.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.DatabaseMapping; import org.eclipse.persistence.mappings.querykeys.ForeignReferenceQueryKey; import org.eclipse.persistence.mappings.querykeys.QueryKey; import org.eclipse.persistence.queries.ReportQuery; /** * This {@link ExpressionVisitor} visits an {@link org.eclipse.persistence.jpa.jpql.parser.Expression * JPQL Expression} and creates the corresponding {@link org.eclipse.persistence.expressions. * Expression EclipseLink Expression}. * * @version 2.6 * @since 2.3 * @author Pascal Filion * @author John Bracken */ @SuppressWarnings("nls") final class ExpressionBuilderVisitor implements EclipseLinkExpressionVisitor { /** * This visitor creates a list by retrieving either the single child or the children of the * {@link CollectionExpression}, which would be the child. */ private ChildrenExpressionVisitor childrenExpressionVisitor; /** * Determines whether the target relationship is allowed to be <code>null</code>. */ private boolean nullAllowed; /** * This {@link Comparator} compares two {@link Class} values and returned the appropriate numeric * type that takes precedence. */ private Comparator<Class<?>> numericTypeComparator; /** * The context used to query information about the application metadata. */ private final JPQLQueryContext queryContext; /** * The EclipseLink {@link Expression} that represents a visited parsed {@link org.eclipse * persistence.jpa.query.parser.Expression Expression} */ private Expression queryExpression; /** * Keeps track of the type of an expression while traversing it. */ private final Class<?>[] type; /** * The visitor responsible to create the {@link Expression Expressions} for the <b>WHEN</b> and * <b>THEN</b> expressions. */ private WhenClauseExpressionVisitor whenClauseExpressionVisitor; /** * Creates a new <code>ExpressionBuilderVisitor</code>. * * @param queryContext The context used to query information about the application metadata and * cached information */ ExpressionBuilderVisitor(JPQLQueryContext queryContext) { super(); this.type = new Class<?>[1]; this.queryContext = queryContext; } private void appendJoinVariables(org.eclipse.persistence.jpa.jpql.parser.Expression expression, ReportQuery subquery) { queryExpression = null; for (String variableName : collectOuterIdentificationVariables()) { Expression innerExpression = queryContext.getQueryExpression(variableName); Expression outerExpression = queryContext.getParent().getQueryExpressionImp(variableName); Expression equalExpression = innerExpression.equal(outerExpression); if (queryExpression == null) { queryExpression = equalExpression; } else { queryExpression = queryExpression.and(equalExpression); } } // Aggregate the WHERE clause with the joins expression if (queryExpression != null) { Expression whereClause = subquery.getSelectionCriteria(); if (whereClause != null) { whereClause = whereClause.and(queryExpression); } else { whereClause = queryExpression; } subquery.setSelectionCriteria(whereClause); } } /** * Creates a new EclipseLink {@link Expression} by visiting the given JPQL {@link org.eclipse. * persistence.jpa.jpql.parser.Expression Expression}. * * @param expression The {@link org.eclipse.persistence.jpa.jpql.parser.Expression Expression} to * convert into an EclipseLink {@link Expression} * @param type The type of the expression * @return The EclipseLink {@link Expression} of the JPQL fragment */ Expression buildExpression(org.eclipse.persistence.jpa.jpql.parser.Expression expression, Class<?>[] type) { Class<?> oldType = this.type[0]; Expression oldQueryExpression = queryExpression; try { this.type[0] = null; this.queryExpression = null; expression.accept(this); type[0] = this.type[0]; return queryExpression; } finally { this.type[0] = oldType; this.queryExpression = oldQueryExpression; } } /** * Creates a new EclipseLink {@link Expression} by visiting the given JPQL {@link * CollectionValuedPathExpression} that is used in the <code><b>GROUP BY</b></code> clause. * * @param expression The {@link CollectionValuedPathExpression} to convert into an EclipseLink * {@link Expression} * @return The EclipseLink {@link Expression} representation of that path expression */ Expression buildGroupByExpression(CollectionValuedPathExpression expression) { try { PathResolver resolver = new PathResolver(); resolver.length = expression.pathSize() - 1; resolver.nullAllowed = false; resolver.checkMappingType = false; expression.accept(resolver); return resolver.localExpression; } finally { this.type[0] = null; this.queryExpression = null; } } /** * Creates a new EclipseLink {@link Expression} by visiting the given JPQL {@link * StateFieldPathExpression}. This method temporarily changes the null allowed flag if the state * field is a foreign reference mapping * * @param expression The {@link StateFieldPathExpression} to convert into an EclipseLink {@link * Expression} * @return The EclipseLink {@link Expression} representation of that path expression */ Expression buildModifiedPathExpression(StateFieldPathExpression expression) { try { PathResolver resolver = new PathResolver(); resolver.length = expression.pathSize(); resolver.checkMappingType = true; expression.accept(resolver); return resolver.localExpression; } finally { this.type[0] = null; this.queryExpression = null; } } /** * Creates a new {@link ReportQuery} by visiting the given {@link SimpleSelectStatement}. * * @param expression The {@link SimpleSelectStatement} to convert into a {@link ReportQuery} * @return A fully initialized {@link ReportQuery} */ ReportQuery buildSubquery(SimpleSelectStatement expression) { // First create the subquery ReportQuery subquery = new ReportQuery(); queryContext.newSubQueryContext(expression, subquery); try { // Visit the subquery to populate it ReportQueryVisitor visitor = new ReportQueryVisitor(queryContext, subquery); expression.accept(visitor); type[0] = visitor.type; // Add the joins between the subquery and the parent query appendJoinVariables(expression, subquery); return subquery; } finally { queryContext.disposeSubqueryContext(); } } private List<org.eclipse.persistence.jpa.jpql.parser.Expression> children(org.eclipse.persistence.jpa.jpql.parser.Expression expression) { ChildrenExpressionVisitor visitor = childrenExpressionVisitor(); try { expression.accept(visitor); return new LinkedList<org.eclipse.persistence.jpa.jpql.parser.Expression>(visitor.expressions); } finally { visitor.expressions.clear(); } } private ChildrenExpressionVisitor childrenExpressionVisitor() { if (childrenExpressionVisitor == null) { childrenExpressionVisitor = new ChildrenExpressionVisitor(); } return childrenExpressionVisitor; } private Set<String> collectOuterIdentificationVariables() { // Retrieve the identification variables used in the current query Set<String> variableNames = new HashSet<String>(queryContext.getUsedIdentificationVariables()); // Now remove the local identification variables that are defined in JOIN expressions and // in collection member declarations for (Declaration declaration : queryContext.getDeclarations()) { variableNames.remove(declaration.getVariableName()); } return variableNames; } /** * {@inheritDoc} */ @Override public void visit(AbsExpression expression) { // First create the expression from the encapsulated expression expression.getExpression().accept(this); // Now create the ABS expression queryExpression = ExpressionMath.abs(queryExpression); // Note: The type will be calculated when traversing the ABS's expression } /** * {@inheritDoc} */ @Override public void visit(AbstractSchemaName expression) { ClassDescriptor descriptor = queryContext.getDescriptor(expression.getText()); type[0] = descriptor.getJavaClass(); queryExpression = new ExpressionBuilder(type[0]); } /** * {@inheritDoc} */ @Override public void visit(AdditionExpression expression) { List<Class<?>> types = new ArrayList<Class<?>>(2); // Create the left side of the addition expression expression.getLeftExpression().accept(this); Expression leftExpression = queryExpression; types.add(type[0]); // Create the right side of the addition expression expression.getRightExpression().accept(this); Expression rightExpression = queryExpression; types.add(type[0]); // Now create the addition expression queryExpression = ExpressionMath.add(leftExpression, rightExpression); // Set the expression type Collections.sort(types, NumericTypeComparator.instance()); type[0] = types.get(0); } /** * {@inheritDoc} */ @Override public void visit(AllOrAnyExpression expression) { // First create the subquery ReportQuery subquery = buildSubquery((SimpleSelectStatement) expression.getExpression()); // Now create the ALL|SOME|ANY expression String identifier = expression.getIdentifier(); queryExpression = queryContext.getBaseExpression(); if (identifier == AllOrAnyExpression.ALL) { queryExpression = queryExpression.all(subquery); } else if (identifier == AllOrAnyExpression.SOME) { queryExpression = queryExpression.some(subquery); } else if (identifier == AllOrAnyExpression.ANY) { queryExpression = queryExpression.any(subquery); } // Note: The type will be calculated when traversing the ABS's expression } /** * {@inheritDoc} */ @Override public void visit(AndExpression expression) { // Create the left side of the logical expression expression.getLeftExpression().accept(this); Expression leftExpression = queryExpression; // Create the right side of the logical expression expression.getRightExpression().accept(this); Expression rightExpression = queryExpression; // Now create the AND expression queryExpression = leftExpression.and(rightExpression); // Set the expression type type[0] = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(ArithmeticFactor expression) { // First create the Expression that is prepended with the unary sign expression.getExpression().accept(this); Expression arithmeticFactor = queryExpression; // Create an expression for the constant 0 (zero) queryExpression = new ConstantExpression(0, new ExpressionBuilder()); // "- <something>" is really "0 - <something>" queryExpression = ExpressionMath.subtract(queryExpression, arithmeticFactor); // Note: The type will be calculated when traversing the sub-expression } /** * {@inheritDoc} */ @Override public void visit(AsOfClause expression) { expression.getExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(AvgFunction expression) { // First create the expression from the encapsulated expression expression.getExpression().accept(this); // Mark the AVG expression distinct if (expression.hasDistinct()) { queryExpression = queryExpression.distinct(); } // Now create the AVG expression queryExpression = queryExpression.average(); // Set the expression type type[0] = Double.class; } /** * {@inheritDoc} */ @Override public void visit(BadExpression expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(BetweenExpression expression) { // First create the Expression for the result expression expression.getExpression().accept(this); Expression resultExpression = queryExpression; // Create the expression for the lower bound expression expression.getLowerBoundExpression().accept(this); Expression lowerBoundExpression = queryExpression; // Create the expression for the upper bound expression expression.getUpperBoundExpression().accept(this); Expression upperBoundExpression = queryExpression; // Create the BETWEEN expression if (expression.hasNot()) { queryExpression = resultExpression.notBetween(lowerBoundExpression, upperBoundExpression); } else { queryExpression = resultExpression.between(lowerBoundExpression, upperBoundExpression); } // Set the expression type type[0] = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(CaseExpression expression) { Expression caseOperandExpression = null; // Create the case operand expression if (expression.hasCaseOperand()) { expression.getCaseOperand().accept(this); caseOperandExpression = queryExpression; } WhenClauseExpressionVisitor visitor = whenClauseExpressionVisitor(); try { // Create the WHEN clauses expression.getWhenClauses().accept(visitor); // Create the ELSE clause expression.getElseExpression().accept(this); Expression elseExpression = queryExpression; visitor.types.add(type[0]); // Create the CASE expression if (caseOperandExpression != null) { queryExpression = caseOperandExpression.caseStatement(visitor.whenClauses, elseExpression); } else { queryExpression = queryContext.getBaseExpression(); queryExpression = queryExpression.caseStatement(visitor.whenClauses, elseExpression); } // Set the expression type type[0] = queryContext.typeResolver().compareCollectionEquivalentTypes(visitor.types); } finally { visitor.dispose(); } } /** * {@inheritDoc} */ @Override public void visit(CastExpression expression) { // First create the expression from the encapsulated expression expression.getExpression().accept(this); // Now create the CAST expression org.eclipse.persistence.jpa.jpql.parser.Expression databaseType = expression.getDatabaseType(); queryExpression = queryExpression.cast(databaseType.toParsedText()); // Set the expression type type[0] = Object.class; } /** * {@inheritDoc} */ @Override public void visit(CoalesceExpression expression) { List<Expression> expressions = new ArrayList<Expression>(); List<Class<?>> types = new LinkedList<Class<?>>(); // Create the Expression for each scalar expression for (org.eclipse.persistence.jpa.jpql.parser.Expression child : expression.getExpression().children()) { child.accept(this); expressions.add(queryExpression); types.add(type[0]); // Set the type on an untyped ParameterExpression, so that // valid types can be passed for null parameter values in JDBC if (queryExpression.isParameterExpression()) { ParameterExpression paramExpression = (ParameterExpression) queryExpression; if (paramExpression.getType() == null || paramExpression.getType().equals(Object.class)) { paramExpression.setType(type[0]); } } } // Create the COALESCE expression queryExpression = queryContext.getBaseExpression(); queryExpression = queryExpression.coalesce(expressions); // Set the expression type type[0] = queryContext.typeResolver().compareCollectionEquivalentTypes(types); } /** * {@inheritDoc} */ @Override public void visit(CollectionExpression expression) { // Nothing to do, this should be handled by the owning expression } /** * {@inheritDoc} */ @Override public void visit(CollectionMemberDeclaration expression) { expression.getCollectionValuedPathExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(CollectionMemberExpression expression) { // Create the expression for the entity expression expression.getEntityExpression().accept(this); Expression entityExpression = queryExpression; if (expression.hasNot()) { CollectionValuedPathExpression pathExpression = (CollectionValuedPathExpression) expression.getCollectionValuedPathExpression(); // Retrieve the ExpressionBuilder from the collection-valued path expression's // identification variable and the variable name pathExpression.getIdentificationVariable().accept(this); Expression parentExpression = queryExpression; // Now create the actual expression Expression newBuilder = new ExpressionBuilder(); Expression collectionBase = newBuilder; for (int i = 1; i < pathExpression.pathSize() - 1; i++) { // nested paths must be single valued. collectionBase = collectionBase.get(pathExpression.getPath(i)); } String lastPath = pathExpression.getPath(pathExpression.pathSize() - 1); // The following code is copied from Expression.noneOf and altered a bit Expression criteria = newBuilder.equal(parentExpression).and(collectionBase.anyOf(lastPath).equal(entityExpression)); ReportQuery subQuery = new ReportQuery(); subQuery.setShouldRetrieveFirstPrimaryKey(true); subQuery.setSelectionCriteria(criteria); // subQuery has the same reference class as parentExpression (which is an ExpressionBuilder). subQuery.setReferenceClass(((ExpressionBuilder)parentExpression).getQueryClass()); queryExpression = parentExpression.notExists(subQuery); } else { // Create the expression for the collection-valued path expression expression.getCollectionValuedPathExpression().accept(this); // Create the MEMBER OF expression queryExpression = queryExpression.equal(entityExpression); } } /** * {@inheritDoc} */ @Override public void visit(CollectionValuedPathExpression expression) { visitPathExpression(expression, nullAllowed, expression.pathSize()); } /** * {@inheritDoc} */ @Override public void visit(ComparisonExpression expression) { ComparisonExpressionVisitor visitor = new ComparisonExpressionVisitor(); // Create the left side of the comparison expression expression.getLeftExpression().accept(visitor); Expression leftExpression = queryExpression; // Create the right side of the comparison expression expression.getRightExpression().accept(visitor); Expression rightExpression = queryExpression; // Now create the comparison expression String comparaison = expression.getComparisonOperator(); // = if (comparaison == ComparisonExpression.EQUAL) { queryExpression = leftExpression.equal(rightExpression); } // <>, != else if (comparaison == ComparisonExpression.DIFFERENT || comparaison == ComparisonExpression.NOT_EQUAL) { queryExpression = leftExpression.notEqual(rightExpression); } // < else if (comparaison == ComparisonExpression.LOWER_THAN) { queryExpression = leftExpression.lessThan(rightExpression); } // <= else if (comparaison == ComparisonExpression.LOWER_THAN_OR_EQUAL) { queryExpression = leftExpression.lessThanEqual(rightExpression); } // > else if (comparaison == ComparisonExpression.GREATER_THAN) { queryExpression = leftExpression.greaterThan(rightExpression); } // <= else if (comparaison == ComparisonExpression.GREATER_THAN_OR_EQUAL) { queryExpression = leftExpression.greaterThanEqual(rightExpression); } else { throw new IllegalArgumentException("The comparison operator is unknown: " + comparaison); } // Set the expression type type[0] = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(ConcatExpression expression) { List<org.eclipse.persistence.jpa.jpql.parser.Expression> expressions = children(expression.getExpression()); Expression newExpression = null; for (org.eclipse.persistence.jpa.jpql.parser.Expression child : expressions) { child.accept(this); if (newExpression == null) { newExpression = queryExpression; } else { newExpression = newExpression.concat(queryExpression); } } queryExpression = newExpression; // Set the expression type type[0] = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(ConnectByClause expression) { expression.getExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(ConstructorExpression expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(CountFunction expression) { // Create the expression from the encapsulated expression expression.getExpression().accept(this); // Mark the expression has distinct if (expression.hasDistinct()) { queryExpression = queryExpression.distinct(); } // Create the COUNT expression queryExpression = queryExpression.count(); // Set the expression type type[0] = Long.class; } /** * {@inheritDoc} */ @Override public void visit(DatabaseType expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(DateTime expression) { if (expression.isJDBCDate()) { queryExpression = queryContext.getBaseExpression(); queryExpression = new DateConstantExpression(expression.getText(), queryExpression); String text = expression.getText(); if (text.startsWith("{d")) { type[0] = Date.class; } else if (text.startsWith("{ts")) { type[0] = Timestamp.class; } else if (text.startsWith("{t")) { type[0] = Time.class; } else { type[0] = Object.class; } } else { queryExpression = queryContext.getBaseExpression(); if (expression.isCurrentDate()) { queryExpression = queryExpression.currentDateDate(); type[0] = Date.class; } else if (expression.isCurrentTime()) { queryExpression = queryExpression.currentTime(); type[0] = Time.class; } else if (expression.isCurrentTimestamp()) { queryExpression = queryExpression.currentTimeStamp(); type[0] = Timestamp.class; } else { throw new IllegalArgumentException("The DateTime is unknown: " + expression); } } } /** * {@inheritDoc} */ @Override public void visit(DeleteClause expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(DeleteStatement expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(DivisionExpression expression) { List<Class<?>> types = new ArrayList<Class<?>>(2); // Create the left side of the division expression expression.getLeftExpression().accept(this); Expression leftExpression = queryExpression; types.add(type[0]); // Create the right side of the division expression expression.getRightExpression().accept(this); Expression rightExpression = queryExpression; types.add(type[0]); // Now create the division expression queryExpression = ExpressionMath.divide(leftExpression, rightExpression); // Set the expression type Collections.sort(types, NumericTypeComparator.instance()); type[0] = types.get(0); } /** * {@inheritDoc} */ @Override public void visit(EmptyCollectionComparisonExpression expression) { CollectionValuedPathExpression collectionPath = (CollectionValuedPathExpression) expression.getExpression(); int lastPathIndex = collectionPath.pathSize() - 1; String name = collectionPath.getPath(lastPathIndex); // Create the expression for the collection-valued path expression (except the last path) visitPathExpression(collectionPath, false, lastPathIndex); // Create the IS EMPTY expression if (expression.hasNot()) { queryExpression = queryExpression.notEmpty(name); } else { queryExpression = queryExpression.isEmpty(name); } // Set the expression type type[0] = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(EntityTypeLiteral expression) { ClassDescriptor descriptor = queryContext.getDescriptor(expression.getEntityTypeName()); type[0] = descriptor.getJavaClass(); queryExpression = new ConstantExpression(type[0], queryContext.getBaseExpression()); } /** * {@inheritDoc} */ @Override public void visit(EntryExpression expression) { // Create the expression for the collection-valued path expression expression.getExpression().accept(this); // Now create the ENTRY expression MapEntryExpression entryExpression = new MapEntryExpression(queryExpression); entryExpression.returnMapEntry(); queryExpression = entryExpression; // Set the expression type type[0] = Map.Entry.class; } /** * {@inheritDoc} */ @Override public void visit(ExistsExpression expression) { // First create the subquery ReportQuery subquery = buildSubquery((SimpleSelectStatement) expression.getExpression()); // Replace the SELECT clause of the exists subquery by SELECT 1 to avoid problems with // databases not supporting multiple columns in the subquery SELECT clause in SQL. The // original select clause expressions might include relationship navigations which should // result in FK joins in the generated SQL, e.g. ... EXISTS (SELECT o.customer FROM Order // o ...). Add the select clause expressions as non fetch join attributes to the ReportQuery // representing the subquery. This make sure the FK joins get generated for (ReportItem item : subquery.getItems()) { Expression expr = item.getAttributeExpression(); subquery.addNonFetchJoinedAttribute(expr); } subquery.clearItems(); Expression one = new ConstantExpression(1, new ExpressionBuilder()); subquery.addItem("one", one); subquery.dontUseDistinct(); // Now create the EXISTS expression queryExpression = queryContext.getBaseExpression(); if (expression.hasNot()) { queryExpression = queryExpression.notExists(subquery); } else { queryExpression = queryExpression.exists(subquery); } // Set the expression type type[0] = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(ExtractExpression expression) { // First create the expression from the encapsulated expression expression.getExpression().accept(this); // Now create the EXTRACT expression queryExpression = queryExpression.extract(expression.getDatePart()); // Set the expression type type[0] = Object.class; } /** * {@inheritDoc} */ @Override public void visit(FromClause expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(FunctionExpression expression) { String identifier = expression.getIdentifier(); String functionName = expression.getUnquotedFunctionName(); // COLUMN if (identifier == org.eclipse.persistence.jpa.jpql.parser.Expression.COLUMN) { // Create the expression for the single child expression.getExpression().accept(this); // Create the expression representing a field in a data-level query queryExpression = queryExpression.getField(functionName); } else { List<org.eclipse.persistence.jpa.jpql.parser.Expression> expressions = children(expression.getExpression()); // No arguments if (expressions.isEmpty()) { queryExpression = queryContext.getBaseExpression(); // OPERATOR if (identifier == org.eclipse.persistence.jpa.jpql.parser.Expression.SQL) { queryExpression = queryExpression.literal(functionName); } // FUNC/FUNCTION else { queryExpression = queryExpression.getFunction(functionName); } } // One or more arguments else { // Create the Expressions for the rest List<Expression> queryExpressions = new ArrayList<Expression>(expressions.size()); for (org.eclipse.persistence.jpa.jpql.parser.Expression child : expressions) { child.accept(this); queryExpressions.add(queryExpression); } queryExpression = queryExpressions.remove(0); // SQL if (identifier == org.eclipse.persistence.jpa.jpql.parser.Expression.SQL) { queryExpression = queryExpression.sql(functionName, queryExpressions); } // OPERATOR else if (identifier == org.eclipse.persistence.jpa.jpql.parser.Expression.OPERATOR) { queryExpression = queryExpression.operator(functionName, queryExpressions); } // FUNC/FUNCTION else { queryExpression = queryExpression.getFunctionWithArguments(functionName, queryExpressions); } } } // Set the expression type type[0] = Object.class; } /** * {@inheritDoc} */ @Override public void visit(GroupByClause expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(HavingClause expression) { expression.getConditionalExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(HierarchicalQueryClause expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(IdentificationVariable expression) { // The identification variable is virtual, only do something // if it falsely represents a state field path expression if (expression.isVirtual()) { StateFieldPathExpression stateFieldPathExpression = expression.getStateFieldPathExpression(); if (stateFieldPathExpression != null) { stateFieldPathExpression.accept(this); return; } } String variableName = expression.getVariableName(); // Identification variable, it's important to use findQueryExpression() and not // getQueryExpression(). If the identification variable is defined by the parent // query, then the ExpressionBuilder have most likely been created already queryExpression = queryContext.findQueryExpression(variableName); // Retrieve the Declaration mapped to the variable name Declaration declaration = queryContext.findDeclaration(variableName); // A null Declaration would most likely mean it's coming from a // state field path expression that represents an enum constant if (declaration != null) { // The Expression was not created yet, which can happen if the identification // variable is declared in a parent query. If that is the case, create the // ExpressionBuilder and cache it for the current query if (queryExpression == null) { declaration.getBaseExpression().accept(this); queryContext.addQueryExpression(variableName, queryExpression); } // Retrieve the Entity type if (declaration.getType() == Type.RANGE) { type[0] = declaration.getDescriptor().getJavaClass(); } } } /** * {@inheritDoc} */ @Override public void visit(IdentificationVariableDeclaration expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(IndexExpression expression) { // Create the expression for the encapsulated expression expression.getExpression().accept(this); // Now create the INDEX expression queryExpression = queryExpression.index(); // Set the expression type type[0] = Integer.class; } /** * {@inheritDoc} */ @Override public void visit(InExpression expression) { // Visit the left expression InExpressionExpressionBuilder visitor1 = new InExpressionExpressionBuilder(); expression.getExpression().accept(visitor1); // Visit the IN items InExpressionBuilder visitor2 = new InExpressionBuilder(); visitor2.hasNot = expression.hasNot(); visitor2.singleInputParameter = expression.isSingleInputParameter(); visitor2.leftExpression = queryExpression; expression.getInItems().accept(visitor2); // Set the expression type type[0] = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(InputParameter expression) { String parameterName = expression.getParameter(); // Calculate the input parameter type Class<?> type = queryContext.getParameterType(expression); // Create the expression queryExpression = queryContext.getBaseExpression(); queryExpression = queryExpression.getParameter(parameterName.substring(1), type); // Cache the input parameter type queryContext.addInputParameter(expression, queryExpression); } /** * {@inheritDoc} */ @Override public void visit(Join expression) { try { nullAllowed = expression.isLeftJoin(); expression.getJoinAssociationPath().accept(this); } finally { nullAllowed = false; } } /** * {@inheritDoc} */ @Override public void visit(JPQLExpression expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(KeyExpression expression) { // First visit the parent Expression expression.getExpression().accept(this); // Now create the Expression of the KEY expression queryExpression = new MapEntryExpression(queryExpression); } /** * {@inheritDoc} */ @Override public void visit(KeywordExpression expression) { String keyword = expression.getText(); Object value; if (keyword == KeywordExpression.NULL) { value = null; type[0] = Object.class; } else if (keyword == KeywordExpression.TRUE) { value = Boolean.TRUE; type[0] = Boolean.class; } else { value = Boolean.FALSE; type[0] = Boolean.class; } queryExpression = queryContext.getBaseExpression(); queryExpression = new ConstantExpression(value, queryExpression); } /** * {@inheritDoc} */ @Override public void visit(LengthExpression expression) { // Create the expression from the encapsulated expression expression.getExpression().accept(this); // Now create the LENGTH expression queryExpression = queryExpression.length(); // Set the expression type type[0] = Integer.class; } /** * {@inheritDoc} */ @Override public void visit(LikeExpression expression) { // Create the first expression expression.getStringExpression().accept(this); Expression firstExpression = queryExpression; // Create the expression for the pattern value expression.getPatternValue().accept(this); Expression patternValue = queryExpression; // Create the LIKE expression with the escape character if (expression.hasEscapeCharacter()) { expression.getEscapeCharacter().accept(this); queryExpression = firstExpression.like(patternValue, queryExpression); } // Create the LIKE expression with no escape character else { queryExpression = firstExpression.like(patternValue); } // Negate the expression if (expression.hasNot()) { queryExpression = queryExpression.not(); } // Set the expression type type[0] = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(LocateExpression expression) { // Create the string to find in the find in expression expression.getFirstExpression().accept(this); Expression findExpression = queryExpression; // Create the find in string expression expression.getSecondExpression().accept(this); Expression findInExpression = queryExpression; // Create the expression for the start position expression.getThirdExpression().accept(this); Expression startPositionExpression = queryExpression; // Create the LOCATE expression if (startPositionExpression != null) { queryExpression = findInExpression.locate(findExpression, startPositionExpression); } else { queryExpression = findInExpression.locate(findExpression); } // Set the expression type type[0] = Integer.class; } /** * {@inheritDoc} */ @Override public void visit(LowerExpression expression) { // Create the expression from the encapsulated expression expression.getExpression().accept(this); // Now create the LOWER expression queryExpression = queryExpression.toLowerCase(); // Set the expression type type[0] = String.class; } /** * {@inheritDoc} */ @Override public void visit(MaxFunction expression) { // Create the expression from the encapsulated expression expression.getExpression().accept(this); // Mark the MAX expression has distinct if (expression.hasDistinct()) { queryExpression = queryExpression.distinct(); } // Now create the MAX expression queryExpression = queryExpression.maximum(); // Note: The type will be calculated when traversing the sub-expression } /** * {@inheritDoc} */ @Override public void visit(MinFunction expression) { // Create the expression from the encapsulated expression expression.getExpression().accept(this); // Mark the MIN expression has distinct if (expression.hasDistinct()) { queryExpression = queryExpression.distinct(); } // Now create the MIN expression queryExpression = queryExpression.minimum(); // Note: The type will be calculated when traversing the sub-expression } /** * {@inheritDoc} */ @Override public void visit(ModExpression expression) { // First create the Expression for the first expression expression.getFirstExpression().accept(this); Expression leftExpression = queryExpression; // Now create the Expression for the second expression expression.getSecondExpression().accept(this); Expression rightExpression = queryExpression; // Now create the MOD expression queryExpression = ExpressionMath.mod(leftExpression, rightExpression); // Set the expression type type[0] = Integer.class; } /** * {@inheritDoc} */ @Override public void visit(MultiplicationExpression expression) { List<Class<?>> types = new ArrayList<Class<?>>(2); // Create the left side of the multiplication expression expression.getLeftExpression().accept(this); Expression leftExpression = queryExpression; types.add(type[0]); // Create the right side of the multiplication expression expression.getRightExpression().accept(this); Expression rightExpression = queryExpression; types.add(type[0]); // Now create the multiplication expression queryExpression = ExpressionMath.multiply(leftExpression, rightExpression); // Set the expression type Collections.sort(types, NumericTypeComparator.instance()); type[0] = types.get(0); } /** * {@inheritDoc} */ @Override public void visit(NotExpression expression) { // Create the expression expression.getExpression().accept(this); // Negate the expression queryExpression = queryExpression.not(); // Set the expression type type[0] = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(NullComparisonExpression expression) { // Create the expression first expression.getExpression().accept(this); // Mark it as NOT NULL if (expression.hasNot()) { queryExpression = queryExpression.notNull(); } // Mark it as IS NULL else { queryExpression = queryExpression.isNull(); } // Set the expression type type[0] = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(NullExpression expression) { queryExpression = null; type[0] = null; } /** * {@inheritDoc} */ @Override public void visit(NullIfExpression expression) { // Create the first expression expression.getFirstExpression().accept(this); Expression firstExpression = queryExpression; Class<?> actualType = type[0]; // Create the second expression expression.getSecondExpression().accept(this); Expression secondExpression = queryExpression; // Now create the NULLIF expression queryExpression = firstExpression.nullIf(secondExpression); // Set the expression type type[0] = actualType; } /** * {@inheritDoc} */ @Override public void visit(NumericLiteral expression) { // Instantiate a Number object with the value type[0] = queryContext.getType(expression); // Special case for a long number, Long.parseLong() does not handle 'l|L' // but Double.parseDouble() and Float.parseFloat() do handle 'd|D' and 'f|F', respectively String text = expression.getText(); if ((type[0] == Long.class) && (text.endsWith("L") || text.endsWith("l"))) { text = text.substring(0, text.length() - 1); } Number number = queryContext.newInstance(type[0], String.class, text); // Now create the numeric expression queryExpression = new ConstantExpression(number, queryContext.getBaseExpression()); } /** * {@inheritDoc} */ @Override public void visit(ObjectExpression expression) { // Simply traverse the OBJECT's expression expression.getExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(OnClause expression) { expression.getConditionalExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(OrderByClause expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(OrderByItem expression) { // Create the item expression.getExpression().accept(this); // Create the ordering item switch (expression.getOrdering()) { case ASC: queryExpression = queryExpression.ascending(); break; case DESC: queryExpression = queryExpression.descending(); break; } // Create the null ordering item switch (expression.getNullOrdering()) { case NULLS_FIRST: queryExpression = queryExpression.nullsFirst(); break; case NULLS_LAST: queryExpression = queryExpression.nullsLast(); break; } } /** * {@inheritDoc} */ @Override public void visit(OrderSiblingsByClause expression) { expression.getOrderByItems().accept(this); } /** * {@inheritDoc} */ @Override public void visit(OrExpression expression) { // Create the left side of the logical expression expression.getLeftExpression().accept(this); Expression leftExpression = queryExpression; // Create the right side of the logical expression expression.getRightExpression().accept(this); Expression rightExpression = queryExpression; // Now create the OR expression queryExpression = leftExpression.or(rightExpression); // Set the expression type type[0] = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(RangeVariableDeclaration expression) { IdentificationVariable variable = (IdentificationVariable) expression.getIdentificationVariable(); Declaration declaration = queryContext.getDeclaration(variable.getVariableName()); switch (declaration.getType()) { // If the Declaration is RangeDeclaration, then retrieve its Descriptor directly, // this will support two cases automatically, the "root" object is // 1) An abstract schema name (entity name) -> parsed as AbstractSchemaName // 2) A fully qualified class name -> parsed as a CollectionValuedPathExpression // that cannot be visited case RANGE: case CLASS_NAME: { type[0] = declaration.getDescriptor().getJavaClass(); queryExpression = new ExpressionBuilder(type[0]); break; } // The FROM subquery needs to be created differently than a regular subquery case SUBQUERY: { type[0] = null; queryExpression = declaration.getQueryExpression(); break; } // This should be a derived path (CollectionValuedPathExpression) or a subquery default: { expression.getRootObject().accept(this); break; } } } /** * {@inheritDoc} */ @Override public void visit(RegexpExpression expression) { // Create the first expression expression.getStringExpression().accept(this); Expression firstExpression = queryExpression; // Create the expression for the pattern value expression.getPatternValue().accept(this); Expression patternValue = queryExpression; // Create the REGEXP expression queryExpression = firstExpression.regexp(patternValue); // Set the expression type type[0] = Boolean.class; } /** * {@inheritDoc} */ @Override public void visit(ResultVariable expression) { expression.getSelectExpression().accept(this); IdentificationVariable identificationVariable = (IdentificationVariable) expression.getResultVariable(); String variableName = identificationVariable.getVariableName(); queryContext.addQueryExpression(variableName, queryExpression); // Note: The type will be calculated when traversing the select expression } /** * {@inheritDoc} */ @Override public void visit(SelectClause expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(SelectStatement expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(SimpleFromClause expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(SimpleSelectClause expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(SimpleSelectStatement expression) { // First create the subquery ReportQuery subquery = buildSubquery(expression); // Now wrap the subquery queryExpression = queryContext.getBaseExpression(); queryExpression = queryExpression.subQuery(subquery); } /** * {@inheritDoc} */ @Override public void visit(SizeExpression expression) { CollectionValuedPathExpression pathExpression = (CollectionValuedPathExpression) expression.getExpression(); int lastIndex = pathExpression.pathSize() - 1; // Create the right chain of expressions visitPathExpression(pathExpression, false, lastIndex - 1); // Now create the SIZE expression String name = pathExpression.getPath(lastIndex); queryExpression = queryExpression.size(name); // Set the expression type type[0] = Integer.class; } /** * {@inheritDoc} */ @Override public void visit(SqrtExpression expression) { // First create the expression from the encapsulated expression expression.getExpression().accept(this); // Now create the SQRT expression queryExpression = ExpressionMath.sqrt(queryExpression); // Set the expression type type[0] = Double.class; } /** * {@inheritDoc} */ @Override public void visit(StartWithClause expression) { expression.getConditionalExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(StateFieldPathExpression expression) { visitPathExpression(expression, false, expression.pathSize()); } /** * {@inheritDoc} */ @Override public void visit(StringLiteral expression) { // Create the expression queryExpression = queryContext.getBaseExpression(); queryExpression = new ConstantExpression(expression.getUnquotedText(), queryExpression); // Set the expression type type[0] = String.class; } /** * {@inheritDoc} */ @Override public void visit(SubExpression expression) { expression.getExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(SubstringExpression expression) { // Create the first expression expression.getFirstExpression().accept(this); Expression firstExpression = queryExpression; // Create the second expression expression.getSecondExpression().accept(this); Expression secondExpression = queryExpression; // Create the third expression expression.getThirdExpression().accept(this); Expression thirdExpression = queryExpression; // Now create the SUBSTRING expression if (thirdExpression != null) { queryExpression = firstExpression.substring(secondExpression, thirdExpression); } else { queryExpression = firstExpression.substring(secondExpression); } // Set the expression type type[0] = String.class; } /** * {@inheritDoc} */ @Override public void visit(SubtractionExpression expression) { List<Class<?>> types = new ArrayList<Class<?>>(2); // Create the left side of the subtraction expression expression.getLeftExpression().accept(this); Expression leftExpression = queryExpression; types.add(type[0]); // Create the right side of the subtraction expression expression.getRightExpression().accept(this); Expression rightExpression = queryExpression; types.add(type[0]); // Now create the subtraction expression queryExpression = ExpressionMath.subtract(leftExpression, rightExpression); // Set the expression type Collections.sort(types, NumericTypeComparator.instance()); type[0] = types.get(0); } /** * {@inheritDoc} */ @Override public void visit(SumFunction expression) { // First create the expression from the encapsulated expression expression.getExpression().accept(this); // Mark the SUM expression distinct if (expression.hasDistinct()) { queryExpression = queryExpression.distinct(); } // Now create the SUM expression queryExpression = queryExpression.sum(); // Set the expression type type[0] = queryContext.typeResolver().convertSumFunctionType(type[0]); } /** * {@inheritDoc} */ @Override public void visit(TableExpression expression) { String tableName = queryContext.literal(expression.getExpression(), LiteralType.STRING_LITERAL); tableName = ExpressionTools.unquote(tableName); queryExpression = queryContext.getBaseExpression().getTable(tableName); } /** * {@inheritDoc} */ @Override public void visit(TableVariableDeclaration expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(TreatExpression expression) { // First visit the parent Expression expression.getCollectionValuedPathExpression().accept(this); // Now downcast the Expression EntityTypeLiteral entityTypeLiteral = (EntityTypeLiteral) expression.getEntityType(); ClassDescriptor entityType = queryContext.getDescriptor(entityTypeLiteral.getEntityTypeName()); queryExpression = queryExpression.treat(entityType.getJavaClass()); } /** * {@inheritDoc} */ @Override public void visit(TrimExpression expression) { // Create the TRIM character expression expression.getTrimCharacter().accept(this); Expression trimCharacter = queryExpression; // Create the string to trim expression.getExpression().accept(this); Expression stringExpression = queryExpression; switch (expression.getSpecification()) { case LEADING: { if (trimCharacter != null) { queryExpression = stringExpression.leftTrim(trimCharacter); } else { queryExpression = stringExpression.leftTrim(); } break; } case TRAILING: { if (trimCharacter != null) { queryExpression = stringExpression.rightTrim(trimCharacter); } else { queryExpression = stringExpression.rightTrim(); } break; } default: { if (trimCharacter != null) { queryExpression = stringExpression.trim(trimCharacter); } else { queryExpression = stringExpression.trim(); } break; } } // Set the expression type type[0] = String.class; } /** * {@inheritDoc} */ @Override public void visit(TypeExpression expression) { // First create the Expression for the identification variable expression.getExpression().accept(this); // Create the TYPE expression queryExpression = queryExpression.type(); // Note: The type will be calculated when traversing the select expression } /** * {@inheritDoc} */ @Override public void visit(UnionClause expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(UnknownExpression expression) { queryExpression = null; } /** * {@inheritDoc} */ @Override public void visit(UpdateClause expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(UpdateItem expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(UpdateStatement expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(UpperExpression expression) { // First create the expression from the encapsulated expression expression.getExpression().accept(this); // Now create the UPPER expression queryExpression = queryExpression.toUpperCase(); // Set the expression type type[0] = String.class; } /** * {@inheritDoc} */ @Override public void visit(ValueExpression expression) { expression.getExpression().accept(this); } /** * {@inheritDoc} */ @Override public void visit(WhenClause expression) { // Nothing to do } /** * {@inheritDoc} */ @Override public void visit(WhereClause expression) { expression.getConditionalExpression().accept(this); } private void visitPathExpression(AbstractPathExpression expression, boolean nullAllowed, int lastIndex) { PathResolver resolver = new PathResolver(); resolver.length = lastIndex; resolver.nullAllowed = nullAllowed; resolver.checkMappingType = false; resolver.localExpression = null; resolver.descriptor = null; expression.accept(resolver); queryExpression = resolver.localExpression; } private WhenClauseExpressionVisitor whenClauseExpressionVisitor() { if (whenClauseExpressionVisitor == null) { whenClauseExpressionVisitor = new WhenClauseExpressionVisitor(); } return whenClauseExpressionVisitor; } // Made static for performance reasons. /** * This visitor creates a list by retrieving either the single child or the children of the * {@link CollectionExpression}, which would be the child. */ private static class ChildrenExpressionVisitor extends AnonymousExpressionVisitor { /** * The list of {@link org.eclipse.persistence.jpa.jpql.parser.Expression Expression} that are * the children of an expression. */ List<org.eclipse.persistence.jpa.jpql.parser.Expression> expressions; /** * Creates a new <code>ChildrenExpressionVisitor</code>. */ ChildrenExpressionVisitor() { super(); expressions = new ArrayList<org.eclipse.persistence.jpa.jpql.parser.Expression>(); } /** * {@inheritDoc} */ @Override public void visit(CollectionExpression expression) { for (org.eclipse.persistence.jpa.jpql.parser.Expression child : expression.children()) { expressions.add(child); } } /** * {@inheritDoc} */ @Override public void visit(NullExpression expression) { // Can't be added to the list } /** * {@inheritDoc} */ @Override protected void visit(org.eclipse.persistence.jpa.jpql.parser.Expression expression) { expressions.add(expression); } } /** * This visitor makes sure to properly handle an entity type literal being parsed as an * identification variable. */ private class ComparisonExpressionVisitor extends EclipseLinkAnonymousExpressionVisitor { /** * {@inheritDoc} */ @Override public void visit(IdentificationVariable expression) { boolean found = false; if (!expression.isVirtual()) { ClassDescriptor descriptor = queryContext.getDescriptor(expression.getText()); // Entity type name if (descriptor != null) { type[0] = descriptor.getJavaClass(); queryExpression = new ConstantExpression(type[0], queryContext.getBaseExpression()); found = true; } } if (!found) { expression.accept(ExpressionBuilderVisitor.this); } } /** * {@inheritDoc} */ @Override protected void visit(org.eclipse.persistence.jpa.jpql.parser.Expression expression) { expression.accept(ExpressionBuilderVisitor.this); } } /** * This visitor takes care of creating the left expression of an <code><b>IN</b></code> expression * and make sure to support nested arrays. */ private class InExpressionExpressionBuilder extends EclipseLinkAnonymousExpressionVisitor { /** * {@inheritDoc} */ @Override public void visit(CollectionExpression expression) { // Assume this is a nested array List<Expression> children = new LinkedList<Expression>(); for (org.eclipse.persistence.jpa.jpql.parser.Expression child : expression.children()) { child.accept(this); children.add(queryExpression); } queryExpression = new ConstantExpression(children, queryContext.getBaseExpression()); } /** * {@inheritDoc} */ @Override public void visit(org.eclipse.persistence.jpa.jpql.parser.Expression expression) { expression.accept(ExpressionBuilderVisitor.this); } /** * {@inheritDoc} */ @Override public void visit(SubExpression expression) { expression.getExpression().accept(this); } } /** * This visitor takes care of creating the <code><b>IN</b></code> expression by visiting the items. */ private class InExpressionBuilder extends EclipseLinkAnonymousExpressionVisitor { private boolean hasNot; private Expression leftExpression; private boolean singleInputParameter; /** * Creates a new <code>InExpressionBuilder</code>. */ InExpressionBuilder() { super(); } /** * {@inheritDoc} */ @Override public void visit(CollectionExpression expression) { Collection<Expression> expressions = new ArrayList<Expression>(); InItemExpressionVisitor visitor = new InItemExpressionVisitor(); for (org.eclipse.persistence.jpa.jpql.parser.Expression child : expression.children()) { child.accept(visitor); expressions.add(queryExpression); } if (hasNot) { queryExpression = leftExpression.notIn(expressions); } else { queryExpression = leftExpression.in(expressions); } } /** * {@inheritDoc} */ @Override public void visit(IdentificationVariable expression) { boolean found = false; if (!expression.isVirtual()) { ClassDescriptor descriptor = queryContext.getDescriptor(expression.getText()); // Entity type name if (descriptor != null) { type[0] = descriptor.getJavaClass(); queryExpression = new ConstantExpression(type[0], queryContext.getBaseExpression()); found = true; } } if (!found) { expression.accept(ExpressionBuilderVisitor.this); } } /** * {@inheritDoc} */ @Override public void visit(InputParameter expression) { if (singleInputParameter) { String parameterName = expression.getParameter(); // Create the expression with Collection as the default type queryExpression = queryContext.getBaseExpression(); queryExpression = queryExpression.getParameter(parameterName.substring(1), Collection.class); // Cache the input parameter type, which is by default Collection queryContext.addInputParameter(expression, queryExpression); if (hasNot) { queryExpression = leftExpression.notIn(queryExpression); } else { queryExpression = leftExpression.in(queryExpression); } } else { visit((org.eclipse.persistence.jpa.jpql.parser.Expression) expression); } } /** * {@inheritDoc} */ @Override protected void visit(org.eclipse.persistence.jpa.jpql.parser.Expression expression) { expression.accept(ExpressionBuilderVisitor.this); Collection<Expression> expressions = new ArrayList<Expression>(); expressions.add(queryExpression); // Now create the IN expression if (hasNot) { queryExpression = leftExpression.notIn(expressions); } else { queryExpression = leftExpression.in(expressions); } } /** * {@inheritDoc} */ @Override public void visit(SimpleSelectStatement expression) { // First create the subquery ReportQuery subquery = buildSubquery(expression); // Now create the IN expression if (hasNot) { queryExpression = leftExpression.notIn(subquery); } else { queryExpression = leftExpression.in(subquery); } } private class InItemExpressionVisitor extends AnonymousExpressionVisitor { /** * {@inheritDoc} */ @Override public void visit(IdentificationVariable expression) { ClassDescriptor descriptor = queryContext.getDescriptor(expression.getVariableName()); queryExpression = queryContext.getBaseExpression(); queryExpression = new ConstantExpression(descriptor.getJavaClass(), queryExpression); } /** * {@inheritDoc} */ @Override public void visit(CollectionExpression expression) { // Assume this is a nested array List<Expression> children = new LinkedList<Expression>(); for (org.eclipse.persistence.jpa.jpql.parser.Expression child : expression.children()) { child.accept(this); children.add(queryExpression); } queryExpression = new ConstantExpression(children, queryContext.getBaseExpression()); } /** * {@inheritDoc} */ @Override public void visit(SubExpression expression) { expression.getExpression().accept(this); } /** * {@inheritDoc} */ @Override protected void visit(org.eclipse.persistence.jpa.jpql.parser.Expression expression) { expression.accept(ExpressionBuilderVisitor.this); } } } private class PathResolver extends AbstractEclipseLinkExpressionVisitor { /** * Determines whether */ boolean checkMappingType; /** * Keeps track of the {@link Declaration} when the identification variable maps to a "root" * object defined in the <code><b>FROM</b></code> clause. */ private Declaration declaration; /** * Keeps track of the descriptor while traversing the path expression. */ private ClassDescriptor descriptor; /** * The actual number of paths within the path expression that will be traversed in order to * create the EclipseLink {@link Expression}. */ int length; /** * The EclipseLink {@link Expression} that was retrieved or created while traversing the path * expression. */ Expression localExpression; /** * Determines whether the target relationship is allowed to be <code>null</code>. */ boolean nullAllowed; /** * Resolves a database column. * * @param expression The path expression representing an identification variable mapping to a * database table followed by the column name */ private void resolveColumn(AbstractPathExpression expression) { String path = expression.getPath(1); localExpression = localExpression.getField(path); } /** * Attempts to resolve the path expression as a fully qualified enum constant. * * @param expression The {@link AbstractPathExpression} that might represent an enum constant * @return <code>true</code> if the path was a fully qualified enum constant; <code>false</code> * if it's an actual path expression */ protected boolean resolveEnumConstant(AbstractPathExpression expression) { String fullPath = expression.toParsedText(); Class<?> enumType = queryContext.getEnumType(fullPath); if (enumType != null) { // Make sure we keep track of the enum type type[0] = enumType; // Retrieve the enum constant String path = expression.getPath(expression.pathSize() - 1); Enum<?> enumConstant = retrieveEnumConstant(enumType, path); // Create the Expression localExpression = new ConstantExpression(enumConstant, new ExpressionBuilder()); return true; } return false; } private void resolvePath(AbstractPathExpression expression) { for (int index = expression.hasVirtualIdentificationVariable() ? 0 : 1, count = length; index < count; index++) { String path = expression.getPath(index); DatabaseMapping mapping = descriptor.getObjectBuilder().getMappingForAttributeName(path); boolean last = (index + 1 == count); boolean collectionMapping = false; // The path is a mapping if (mapping != null) { // Make sure we keep track of its type if (type != null) { type[0] = queryContext.calculateMappingType(mapping); } // This will tell us how to create the Expression collectionMapping = mapping.isCollectionMapping(); // Retrieve the reference descriptor so we can continue traversing the path if (!last) { descriptor = mapping.getReferenceDescriptor(); } // Flag that needs to be modified for a special case else if (checkMappingType) { nullAllowed = mapping.isForeignReferenceMapping(); } } // No mapping is defined for the single path, check for a query key else { QueryKey queryKey = descriptor.getQueryKeyNamed(path); if (queryKey != null) { // Make sure we keep track of its type if (type != null) { type[0] = queryContext.calculateQueryKeyType(queryKey); } // This will tell us how to create the Expression collectionMapping = queryKey.isCollectionQueryKey(); // Retrieve the reference descriptor so we can continue traversing the path if (!last && queryKey.isForeignReferenceQueryKey()) { ForeignReferenceQueryKey referenceQueryKey = (ForeignReferenceQueryKey) queryKey; descriptor = queryContext.getDescriptor(referenceQueryKey.getReferenceClass()); } } // Nothing was found else { break; } } // Now create the Expression if (collectionMapping) { if (last && nullAllowed) { localExpression = localExpression.anyOfAllowingNone(path); } else { localExpression = localExpression.anyOf(path); } } else { if (last && nullAllowed) { localExpression = localExpression.getAllowingNull(path); } else { localExpression = localExpression.get(path); } } } } private void resolveVirtualPath(AbstractPathExpression expression) { String path = expression.getPath(1); localExpression = localExpression.get(path); } /** * Retrieves the actual {@link Enum} constant with the given name. * * @param type The {@link Enum} class used to retrieve the given name * @param name The name of the constant to retrieve * @return The {@link Enum} constant */ private Enum<?> retrieveEnumConstant(Class<?> type, String name) { for (Enum<?> enumConstant : (Enum<?>[]) type.getEnumConstants()) { if (name.equals(enumConstant.name())) { return enumConstant; } } return null; } /** * {@inheritDoc} */ @Override public void visit(CollectionValuedPathExpression expression) { visitPathExpression(expression); } /** * {@inheritDoc} */ @Override public void visit(EntryExpression expression) { IdentificationVariable identificationVariable = (IdentificationVariable) expression.getExpression(); String variableName = identificationVariable.getVariableName(); // Retrieve the Expression for the identification variable declaration = queryContext.findDeclaration(variableName); declaration.getBaseExpression().accept(ExpressionBuilderVisitor.this); localExpression = queryExpression; // Create the Map.Entry expression MapEntryExpression entryExpression = new MapEntryExpression(localExpression); entryExpression.returnMapEntry(); localExpression = entryExpression; } /** * {@inheritDoc} */ @Override public void visit(IdentificationVariable expression) { expression.accept(ExpressionBuilderVisitor.this); localExpression = queryExpression; // It is possible the Expression is null, it happens when the path expression is an enum // constant. If so, then no need to retrieve the descriptor if (localExpression != null) { declaration = queryContext.findDeclaration(expression.getVariableName()); descriptor = declaration.getDescriptor(); } } /** * {@inheritDoc} */ @Override public void visit(KeyExpression expression) { IdentificationVariable identificationVariable = (IdentificationVariable) expression.getExpression(); // Create the Expression for the identification variable identificationVariable.accept(ExpressionBuilderVisitor.this); localExpression = new MapEntryExpression(queryExpression); // Retrieve the mapping's key mapping's descriptor descriptor = queryContext.resolveDescriptor(expression); } /** * {@inheritDoc} */ @Override public void visit(StateFieldPathExpression expression) { visitPathExpression(expression); } /** * {@inheritDoc} */ @Override public void visit(TreatExpression expression) { expression.accept(ExpressionBuilderVisitor.this); localExpression = queryExpression; // Retrieve the mapping's key mapping's descriptor descriptor = queryContext.resolveDescriptor(expression); } /** * {@inheritDoc} */ @Override public void visit(ValueExpression expression) { IdentificationVariable identificationVariable = (IdentificationVariable) expression.getExpression(); // Create the Expression for the identification variable identificationVariable.accept(ExpressionBuilderVisitor.this); localExpression = queryExpression; // Retrieve the mapping's reference descriptor declaration = queryContext.findDeclaration(identificationVariable.getVariableName()); descriptor = declaration.getDescriptor(); } private void visitPathExpression(AbstractPathExpression expression) { // First resolve the identification variable expression.getIdentificationVariable().accept(this); // The path expression is composed with an identification // variable that is mapped to a subquery as the "root" object if ((declaration != null) && (declaration.getType() == Type.SUBQUERY)) { resolveVirtualPath(expression); return; } // A null value would most likely mean it's coming from a // state field path expression that represents an enum constant if (localExpression == null) { boolean result = resolveEnumConstant(expression); if (result) { return; } } // The path expression is mapping to a database table if ((declaration != null) && (declaration.getType() == Type.TABLE)) { resolveColumn(expression); return; } // Traverse the rest of the path expression resolvePath(expression); } } /** * This visitor is responsible to create the {@link Expression Expressions} for the <b>WHEN</b> * and <b>THEN</b> expressions. */ private class WhenClauseExpressionVisitor extends AbstractExpressionVisitor { /** * Keeps tracks of the type of each <code><b>WHEN</b></code> clauses. */ final List<Class<?>> types; /** * The map of <b>WHEN</b> expressions mapped to their associated <b>THEN</b> expression. */ Map<Expression, Expression> whenClauses; /** * Creates a new <code>WhenClauseExpressionVisitor</code>. */ WhenClauseExpressionVisitor() { super(); types = new LinkedList<Class<?>>(); whenClauses = new LinkedHashMap<Expression, Expression>(); } /** * Disposes this visitor. */ void dispose() { types.clear(); whenClauses.clear(); } /** * {@inheritDoc} */ @Override public void visit(CollectionExpression expression) { expression.acceptChildren(this); } /** * {@inheritDoc} */ @Override public void visit(WhenClause expression) { // Create the WHEN expression expression.getWhenExpression().accept(ExpressionBuilderVisitor.this); Expression whenExpression = queryExpression; types.add(type[0]); // Create the THEN expression expression.getThenExpression().accept(ExpressionBuilderVisitor.this); Expression thenExpression = queryExpression; whenClauses.put(whenExpression, thenExpression); } } }