| /* |
| * Copyright (c) 2006, 2021 Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 2006, 2021 IBM Corporation. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation |
| // |
| // 04/11/2017-2.6 Will Dazey |
| // - 512386: Concat expression return type Boolean -> String |
| // 02/20/2018-2.7 Will Dazey |
| // - 531062: Incorrect expression type created for CollectionExpression |
| // 05/11/2018-2.7 Will Dazey |
| // - 534515: Incorrect return type set for CASE functions |
| // IBM - Bug 537795: CASE THEN and ELSE scalar expression Constants should not be casted to CASE operand type |
| 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 java.util.Vector; |
| |
| 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 org.eclipse.persistence.jpa.jpql.parser.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<>(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<>(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; |
| } |
| |
| @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 |
| } |
| |
| @Override |
| public void visit(AbstractSchemaName expression) { |
| ClassDescriptor descriptor = queryContext.getDescriptor(expression.getText()); |
| type[0] = descriptor.getJavaClass(); |
| queryExpression = new ExpressionBuilder(type[0]); |
| } |
| |
| @Override |
| public void visit(AdditionExpression expression) { |
| |
| List<Class<?>> types = new ArrayList<>(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); |
| } |
| |
| @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 |
| } |
| |
| @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; |
| } |
| |
| @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 |
| } |
| |
| @Override |
| public void visit(AsOfClause expression) { |
| expression.getExpression().accept(this); |
| } |
| |
| @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; |
| } |
| |
| @Override |
| public void visit(BadExpression expression) { |
| // Nothing to do |
| } |
| |
| @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; |
| } |
| |
| @Override |
| public void visit(CaseExpression expression) { |
| |
| Expression caseBaseExpression = queryExpression; |
| |
| 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); |
| |
| //Bug 537795 |
| //After we build the caseStatement, we need to retroactively fix the THEN/ELSE children's base |
| if(queryExpression.isFunctionExpression()) { |
| Vector<Expression> children = ((org.eclipse.persistence.internal.expressions.FunctionExpression)queryExpression).getChildren(); |
| int index = 1; |
| while(index < children.size()) { |
| Expression when_else = children.get(index); |
| if(index+1 < children.size()) { |
| //Not at end, must be a THEN |
| children.get(index+1).setLocalBase(caseBaseExpression); |
| } else { |
| //At end, must be an ELSE |
| when_else.setLocalBase(caseBaseExpression); |
| } |
| index=index+2; |
| } |
| } |
| } |
| else { |
| queryExpression = queryContext.getBaseExpression(); |
| queryExpression = queryExpression.caseStatement(visitor.whenClauses, elseExpression); |
| } |
| |
| // Set the expression type |
| type[0] = queryContext.typeResolver().compareCollectionEquivalentTypes(visitor.types); |
| } |
| finally { |
| visitor.dispose(); |
| } |
| } |
| |
| @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; |
| } |
| |
| @Override |
| public void visit(CoalesceExpression expression) { |
| |
| List<Expression> expressions = new ArrayList<>(); |
| List<Class<?>> types = new LinkedList<>(); |
| |
| //cache the type of the expression so untyped children have a default type |
| Class<?> coalesceType = type[0]; |
| |
| // 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); |
| |
| //get the expression type parsed from the child expression |
| Class<?> childType = type[0]; |
| |
| // Default the type on an untyped ParameterExpression to the cached expression type. |
| // This is to help provide a valid type for null parameter when binding JDBC parameter types |
| if (queryExpression.isParameterExpression()) { |
| ParameterExpression paramExpression = (ParameterExpression) queryExpression; |
| if (paramExpression.getType() == null || paramExpression.getType().equals(Object.class)) { |
| paramExpression.setType(coalesceType); |
| childType = coalesceType; |
| } |
| } |
| |
| types.add(childType); |
| } |
| |
| // Create the COALESCE expression |
| queryExpression = queryContext.getBaseExpression(); |
| queryExpression = queryExpression.coalesce(expressions); |
| |
| // Set the expression type |
| type[0] = queryContext.typeResolver().compareCollectionEquivalentTypes(types); |
| } |
| |
| @Override |
| public void visit(CollectionExpression expression) { |
| // Nothing to do, this should be handled by the owning expression |
| } |
| |
| @Override |
| public void visit(CollectionMemberDeclaration expression) { |
| expression.getCollectionValuedPathExpression().accept(this); |
| } |
| |
| @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); |
| } |
| } |
| |
| @Override |
| public void visit(CollectionValuedPathExpression expression) { |
| visitPathExpression(expression, nullAllowed, expression.pathSize()); |
| } |
| |
| @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; |
| } |
| |
| @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] = String.class; |
| } |
| |
| @Override |
| public void visit(ConnectByClause expression) { |
| expression.getExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(ConstructorExpression expression) { |
| // Nothing to do |
| } |
| |
| @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; |
| } |
| |
| @Override |
| public void visit(DatabaseType expression) { |
| // Nothing to do |
| } |
| |
| @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); |
| } |
| } |
| } |
| |
| @Override |
| public void visit(DeleteClause expression) { |
| // Nothing to do |
| } |
| |
| @Override |
| public void visit(DeleteStatement expression) { |
| // Nothing to do |
| } |
| |
| @Override |
| public void visit(DivisionExpression expression) { |
| |
| List<Class<?>> types = new ArrayList<>(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); |
| } |
| |
| @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; |
| } |
| |
| @Override |
| public void visit(EntityTypeLiteral expression) { |
| ClassDescriptor descriptor = queryContext.getDescriptor(expression.getEntityTypeName()); |
| type[0] = descriptor.getJavaClass(); |
| queryExpression = new ConstantExpression(type[0], queryContext.getBaseExpression()); |
| } |
| |
| @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; |
| } |
| |
| @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; |
| } |
| |
| @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; |
| } |
| |
| @Override |
| public void visit(FromClause expression) { |
| // Nothing to do |
| } |
| |
| @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<>(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; |
| } |
| |
| @Override |
| public void visit(GroupByClause expression) { |
| // Nothing to do |
| } |
| |
| @Override |
| public void visit(HavingClause expression) { |
| expression.getConditionalExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(HierarchicalQueryClause expression) { |
| // Nothing to do |
| } |
| |
| @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(); |
| } |
| } |
| } |
| |
| @Override |
| public void visit(IdentificationVariableDeclaration expression) { |
| // Nothing to do |
| } |
| |
| @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; |
| } |
| |
| @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; |
| } |
| |
| @Override |
| public void visit(InputParameter expression) { |
| |
| String parameterName = expression.getParameter(); |
| |
| // Calculate the input parameter type |
| type[0] = queryContext.getParameterType(expression); |
| |
| // Create the expression |
| queryExpression = queryContext.getBaseExpression(); |
| queryExpression = queryExpression.getParameter(parameterName.substring(1), type[0]); |
| |
| // Cache the input parameter type |
| queryContext.addInputParameter(expression, queryExpression); |
| } |
| |
| @Override |
| public void visit(Join expression) { |
| try { |
| nullAllowed = expression.isLeftJoin(); |
| expression.getJoinAssociationPath().accept(this); |
| } |
| finally { |
| nullAllowed = false; |
| } |
| } |
| |
| @Override |
| public void visit(JPQLExpression expression) { |
| // Nothing to do |
| } |
| |
| @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); |
| } |
| |
| @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); |
| } |
| |
| @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; |
| } |
| |
| @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; |
| } |
| |
| @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; |
| } |
| |
| @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; |
| } |
| |
| @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 |
| } |
| |
| @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 |
| } |
| |
| @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; |
| } |
| |
| @Override |
| public void visit(MultiplicationExpression expression) { |
| |
| List<Class<?>> types = new ArrayList<>(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); |
| } |
| |
| @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; |
| } |
| |
| @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; |
| } |
| |
| @Override |
| public void visit(NullExpression expression) { |
| queryExpression = null; |
| type[0] = null; |
| } |
| |
| @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; |
| } |
| |
| @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); |
| } |
| |
| @SuppressWarnings({"unchecked"}) |
| Number number = queryContext.newInstance((Class<? extends Number>) type[0], String.class, text); |
| |
| // Now create the numeric expression |
| queryExpression = new ConstantExpression(number, queryContext.getBaseExpression()); |
| } |
| |
| @Override |
| public void visit(ObjectExpression expression) { |
| // Simply traverse the OBJECT's expression |
| expression.getExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(OnClause expression) { |
| expression.getConditionalExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(OrderByClause expression) { |
| // Nothing to do |
| } |
| |
| @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; |
| } |
| } |
| |
| @Override |
| public void visit(OrderSiblingsByClause expression) { |
| expression.getOrderByItems().accept(this); |
| } |
| |
| @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; |
| } |
| |
| @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; |
| } |
| } |
| } |
| |
| @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; |
| } |
| |
| @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 |
| } |
| |
| @Override |
| public void visit(SelectClause expression) { |
| // Nothing to do |
| } |
| |
| @Override |
| public void visit(SelectStatement expression) { |
| // Nothing to do |
| } |
| |
| @Override |
| public void visit(SimpleFromClause expression) { |
| // Nothing to do |
| } |
| |
| @Override |
| public void visit(SimpleSelectClause expression) { |
| // Nothing to do |
| } |
| |
| @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); |
| } |
| |
| @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; |
| } |
| |
| @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; |
| } |
| |
| @Override |
| public void visit(StartWithClause expression) { |
| expression.getConditionalExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(StateFieldPathExpression expression) { |
| visitPathExpression(expression, false, expression.pathSize()); |
| } |
| |
| @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; |
| } |
| |
| @Override |
| public void visit(SubExpression expression) { |
| expression.getExpression().accept(this); |
| } |
| |
| @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; |
| } |
| |
| @Override |
| public void visit(SubtractionExpression expression) { |
| |
| List<Class<?>> types = new ArrayList<>(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); |
| } |
| |
| @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]); |
| } |
| |
| @Override |
| public void visit(TableExpression expression) { |
| String tableName = queryContext.literal(expression.getExpression(), LiteralType.STRING_LITERAL); |
| tableName = ExpressionTools.unquote(tableName); |
| queryExpression = queryContext.getBaseExpression().getTable(tableName); |
| } |
| |
| @Override |
| public void visit(TableVariableDeclaration expression) { |
| // Nothing to do |
| } |
| |
| @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()); |
| } |
| |
| @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; |
| } |
| |
| @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 |
| } |
| |
| @Override |
| public void visit(UnionClause expression) { |
| // Nothing to do |
| } |
| |
| @Override |
| public void visit(UnknownExpression expression) { |
| queryExpression = null; |
| } |
| |
| @Override |
| public void visit(UpdateClause expression) { |
| // Nothing to do |
| } |
| |
| @Override |
| public void visit(UpdateItem expression) { |
| // Nothing to do |
| } |
| |
| @Override |
| public void visit(UpdateStatement expression) { |
| // Nothing to do |
| } |
| |
| @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; |
| } |
| |
| @Override |
| public void visit(ValueExpression expression) { |
| expression.getExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(WhenClause expression) { |
| // Nothing to do |
| } |
| |
| @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<>(); |
| } |
| |
| @Override |
| public void visit(CollectionExpression expression) { |
| for (org.eclipse.persistence.jpa.jpql.parser.Expression child : expression.children()) { |
| expressions.add(child); |
| } |
| } |
| |
| @Override |
| public void visit(NullExpression expression) { |
| // Can't be added to the list |
| } |
| |
| @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 { |
| |
| @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); |
| } |
| } |
| |
| @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 { |
| |
| @Override |
| public void visit(CollectionExpression expression) { |
| |
| // Assume this is a nested array |
| List<Expression> children = new LinkedList<>(); |
| |
| for (org.eclipse.persistence.jpa.jpql.parser.Expression child : expression.children()) { |
| child.accept(this); |
| children.add(queryExpression); |
| } |
| |
| queryExpression = new org.eclipse.persistence.internal.expressions.CollectionExpression(children, queryContext.getBaseExpression()); |
| } |
| |
| @Override |
| public void visit(org.eclipse.persistence.jpa.jpql.parser.Expression expression) { |
| expression.accept(ExpressionBuilderVisitor.this); |
| } |
| |
| @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(); |
| } |
| |
| @Override |
| public void visit(CollectionExpression expression) { |
| |
| Collection<Expression> expressions = new ArrayList<>(); |
| 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); |
| } |
| } |
| |
| @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); |
| } |
| } |
| |
| @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); |
| } |
| } |
| |
| @Override |
| protected void visit(org.eclipse.persistence.jpa.jpql.parser.Expression expression) { |
| |
| expression.accept(ExpressionBuilderVisitor.this); |
| |
| Collection<Expression> expressions = new ArrayList<>(); |
| expressions.add(queryExpression); |
| |
| // Now create the IN expression |
| if (hasNot) { |
| queryExpression = leftExpression.notIn(expressions); |
| } |
| else { |
| queryExpression = leftExpression.in(expressions); |
| } |
| } |
| |
| @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 { |
| |
| |
| @Override |
| public void visit(IdentificationVariable expression) { |
| ClassDescriptor descriptor = queryContext.getDescriptor(expression.getVariableName()); |
| queryExpression = queryContext.getBaseExpression(); |
| queryExpression = new ConstantExpression(descriptor.getJavaClass(), queryExpression); |
| } |
| |
| |
| @Override |
| public void visit(CollectionExpression expression) { |
| |
| // Assume this is a nested array |
| List<Expression> children = new LinkedList<>(); |
| |
| for (org.eclipse.persistence.jpa.jpql.parser.Expression child : expression.children()) { |
| child.accept(this); |
| children.add(queryExpression); |
| } |
| |
| queryExpression = new org.eclipse.persistence.internal.expressions.CollectionExpression(children, queryContext.getBaseExpression()); |
| } |
| |
| |
| @Override |
| public void visit(SubExpression expression) { |
| expression.getExpression().accept(this); |
| } |
| |
| |
| @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; |
| } |
| |
| @Override |
| public void visit(CollectionValuedPathExpression expression) { |
| visitPathExpression(expression); |
| } |
| |
| @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; |
| } |
| |
| @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(); |
| } |
| } |
| |
| @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); |
| } |
| |
| @Override |
| public void visit(StateFieldPathExpression expression) { |
| visitPathExpression(expression); |
| } |
| |
| @Override |
| public void visit(TreatExpression expression) { |
| |
| expression.accept(ExpressionBuilderVisitor.this); |
| localExpression = queryExpression; |
| |
| // Retrieve the mapping's key mapping's descriptor |
| descriptor = queryContext.resolveDescriptor(expression); |
| } |
| |
| @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<>(); |
| whenClauses = new LinkedHashMap<>(); |
| } |
| |
| /** |
| * Disposes this visitor. |
| */ |
| void dispose() { |
| types.clear(); |
| whenClauses.clear(); |
| } |
| |
| @Override |
| public void visit(CollectionExpression expression) { |
| expression.acceptChildren(this); |
| } |
| |
| @Override |
| public void visit(WhenClause expression) { |
| |
| // Create the WHEN expression |
| expression.getWhenExpression().accept(ExpressionBuilderVisitor.this); |
| Expression whenExpression = queryExpression; |
| |
| // Create the THEN expression |
| expression.getThenExpression().accept(ExpressionBuilderVisitor.this); |
| Expression thenExpression = queryExpression; |
| types.add(type[0]); |
| |
| whenClauses.put(whenExpression, thenExpression); |
| } |
| } |
| } |