| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.internal.expressions; |
| |
| import java.io.*; |
| import java.util.*; |
| import org.eclipse.persistence.exceptions.*; |
| import org.eclipse.persistence.history.*; |
| import org.eclipse.persistence.internal.helper.*; |
| import org.eclipse.persistence.expressions.*; |
| import org.eclipse.persistence.internal.databaseaccess.*; |
| |
| /** |
| * Abstract class for expression that have exactly two children, such as and/or and relations. |
| */ |
| public abstract class CompoundExpression extends Expression { |
| protected ExpressionOperator operator; |
| protected transient ExpressionOperator platformOperator; |
| protected Expression firstChild; |
| protected Expression secondChild; |
| protected ExpressionBuilder builder; |
| |
| protected CompoundExpression() { |
| super(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if the expression is equal to the other. |
| * This is used to allow dynamic expression's SQL to be cached. |
| */ |
| @Override |
| public boolean equals(Object object) { |
| if (this == object) { |
| return true; |
| } |
| if (!super.equals(object)) { |
| return false; |
| } |
| CompoundExpression expression = (CompoundExpression) object; |
| return ((this.operator == expression.operator) || ((this.operator != null) && this.operator.equals(expression.operator))) |
| && ((this.firstChild == expression.firstChild) || ((this.firstChild != null) && this.firstChild.equals(expression.firstChild))) |
| && ((this.secondChild == expression.secondChild) || ((this.secondChild != null) && this.secondChild.equals(expression.secondChild))); |
| } |
| |
| /** |
| * INTERNAL: |
| * Compute a consistent hash-code for the expression. |
| * This is used to allow dynamic expression's SQL to be cached. |
| */ |
| @Override |
| public int computeHashCode() { |
| int hashCode = super.computeHashCode(); |
| if (this.operator != null) { |
| hashCode = hashCode + this.operator.hashCode(); |
| } |
| if (this.firstChild != null) { |
| hashCode = hashCode + this.firstChild.hashCode(); |
| } |
| if (this.secondChild != null) { |
| hashCode = hashCode + this.secondChild.hashCode(); |
| } |
| return hashCode; |
| } |
| |
| /** |
| * INTERNAL: |
| * Find the alias for a given table from the first or second child in the additionalOuterJoinCriteria |
| */ |
| @Override |
| public DatabaseTable aliasForTable(DatabaseTable table) { |
| DatabaseTable alias = null; |
| if (this.firstChild != null) { |
| alias = this.firstChild.aliasForTable(table); |
| } |
| |
| if ((alias == null) && (this.secondChild != null)) { |
| alias = this.secondChild.aliasForTable(table); |
| } |
| |
| return alias; |
| } |
| |
| @Override |
| public Expression asOf(AsOfClause clause) { |
| final AsOfClause finalClause = clause; |
| ExpressionIterator iterator = new ExpressionIterator() { |
| @Override |
| public void iterate(Expression each) { |
| if (each.isDataExpression()) { |
| each.asOf(finalClause); |
| } |
| } |
| |
| @Override |
| public boolean shouldIterateOverSubSelects() { |
| return true; |
| } |
| }; |
| iterator.iterateOn(this); |
| return this; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public Expression create(Expression base, Object singleArgument, ExpressionOperator operator) { |
| setFirstChild(base); |
| Expression argument = Expression.from(singleArgument, base); |
| setSecondChild(argument); |
| setOperator(operator); |
| return this; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public Expression create(Expression base, List arguments, ExpressionOperator operator) { |
| setFirstChild(base); |
| if (!arguments.isEmpty()) { |
| setSecondChild((Expression)arguments.get(0)); |
| } |
| setOperator(operator); |
| return this; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for debug printing. |
| */ |
| @Override |
| public String descriptionOfNodeType() { |
| return "Compound Expression"; |
| } |
| |
| /** |
| * Return the expression builder which is the ultimate base of this expression, or |
| * null if there isn't one (shouldn't happen if we start from a root) |
| */ |
| @Override |
| public ExpressionBuilder getBuilder() { |
| // PERF: Cache builder. |
| if (this.builder == null) { |
| this.builder = this.firstChild.getBuilder(); |
| if (this.builder == null) { |
| this.builder = this.secondChild.getBuilder(); |
| } |
| } |
| return this.builder; |
| } |
| |
| public Expression getFirstChild() { |
| return firstChild; |
| } |
| |
| @Override |
| public ExpressionOperator getOperator() { |
| return operator; |
| } |
| |
| public ExpressionOperator getPlatformOperator(DatabasePlatform platform) { |
| if (platformOperator == null) { |
| initializePlatformOperator(platform); |
| } |
| return platformOperator; |
| } |
| |
| public Expression getSecondChild() { |
| return secondChild; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public void initializePlatformOperator(DatabasePlatform platform) { |
| if (this.operator.isComplete()) { |
| platformOperator = this.operator; |
| return; |
| } |
| platformOperator = platform.getOperator(this.operator.getSelector()); |
| if (platformOperator == null) { |
| throw QueryException.invalidOperator(this.operator.toString()); |
| } |
| } |
| |
| @Override |
| public boolean isCompoundExpression() { |
| return true; |
| } |
| |
| /** |
| * INTERNAL: |
| * For iterating using an inner class |
| */ |
| @Override |
| public void iterateOn(ExpressionIterator iterator) { |
| super.iterateOn(iterator); |
| if (this.firstChild != null) { |
| this.firstChild.iterateOn(iterator); |
| } |
| if (this.secondChild != null) { |
| this.secondChild.iterateOn(iterator); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Normalize into a structure that is printable. |
| * Also compute printing information such as outer joins. |
| */ |
| @Override |
| public Expression normalize(ExpressionNormalizer normalizer) { |
| validateNode(); |
| boolean previous = normalizer.isAddAdditionalExpressionsWithinCurrrentExpressionContext(); |
| boolean isOrExpression = (isLogicalExpression() && this.operator.getSelector() == ExpressionOperator.Or); |
| normalizer.setAddAdditionalExpressionsWithinCurrrentExpressionContext(previous|| isOrExpression); |
| |
| try { |
| if (this.firstChild != null) { |
| //let's make sure a session is available in the case of a parallel expression |
| ExpressionBuilder builder = this.firstChild.getBuilder(); |
| if (builder != null){ |
| builder.setSession(normalizer.getSession().getRootSession(null)); |
| } |
| setFirstChild(normalizer.processAdditionalLocalExpressions(this.firstChild.normalize(normalizer), isOrExpression)); |
| } |
| if (this.secondChild != null) { |
| //let's make sure a session is available in the case of a parallel expression |
| ExpressionBuilder builder = this.secondChild.getBuilder(); |
| if (builder != null){ |
| builder.setSession(normalizer.getSession().getRootSession(null)); |
| } |
| setSecondChild(normalizer.processAdditionalLocalExpressions(this.secondChild.normalize(normalizer), isOrExpression)); |
| } |
| } finally { |
| normalizer.setAddAdditionalExpressionsWithinCurrrentExpressionContext(previous); |
| } |
| |
| // For CR2456, it is now possible for normalize to remove redundant |
| // conditions from the where clause. |
| if (this.firstChild == null) { |
| return this.secondChild; |
| } else if (this.secondChild == null) { |
| return this.firstChild; |
| } |
| return this; |
| } |
| |
| /** |
| * Do any required validation for this node. Throw an exception if it's incorrect. |
| * Ensure that both sides are not data expressions. |
| */ |
| @Override |
| public void validateNode() { |
| if (this.firstChild != null) { |
| if (this.firstChild.isDataExpression() || this.firstChild.isConstantExpression()) { |
| throw QueryException.invalidExpression(this); |
| } |
| } |
| if (this.secondChild != null) { |
| if (this.secondChild.isDataExpression() || this.secondChild.isConstantExpression()) { |
| throw QueryException.invalidExpression(this); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for cloning. |
| */ |
| @Override |
| protected void postCopyIn(Map alreadyDone) { |
| super.postCopyIn(alreadyDone); |
| if (this.firstChild != null) { |
| setFirstChild(this.firstChild.copiedVersionFrom(alreadyDone)); |
| } |
| if (this.secondChild != null) { |
| setSecondChild(this.secondChild.copiedVersionFrom(alreadyDone)); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Print SQL |
| */ |
| @Override |
| public void printSQL(ExpressionSQLPrinter printer) { |
| ExpressionOperator realOperator = getPlatformOperator(printer.getPlatform()); |
| printer.printString("("); |
| realOperator.printDuo(this.firstChild, this.secondChild, printer); |
| printer.printString(")"); |
| } |
| |
| /** |
| * INTERNAL: |
| * Print java for project class generation |
| */ |
| @Override |
| public void printJava(ExpressionJavaPrinter printer) { |
| ExpressionOperator realOperator = getPlatformOperator(printer.getPlatform()); |
| realOperator.printJavaDuo(this.firstChild, this.secondChild, printer); |
| } |
| |
| /** |
| * INTERNAL: |
| * This expression is built on a different base than the one we want. Rebuild it and |
| * return the root of the new tree |
| */ |
| @Override |
| public Expression rebuildOn(Expression newBase) { |
| Vector arguments; |
| |
| Expression first = this.firstChild.rebuildOn(newBase); |
| if (this.secondChild == null) { |
| arguments = NonSynchronizedVector.newInstance(0); |
| } else { |
| arguments = NonSynchronizedVector.newInstance(1); |
| arguments.add(this.secondChild.rebuildOn(newBase)); |
| } |
| return first.performOperator(this.operator, arguments); |
| } |
| |
| /** |
| * INTERNAL: |
| * Search the tree for any expressions (like SubSelectExpressions) that have been |
| * built using a builder that is not attached to the query. This happens in case of an Exists |
| * call using a new ExpressionBuilder(). This builder needs to be replaced with one from the query. |
| */ |
| @Override |
| public void resetPlaceHolderBuilder(ExpressionBuilder queryBuilder){ |
| this.firstChild.resetPlaceHolderBuilder(queryBuilder); |
| if (this.secondChild != null){ |
| this.secondChild.resetPlaceHolderBuilder(queryBuilder); |
| } |
| } |
| |
| protected void setFirstChild(Expression firstChild) { |
| this.firstChild = firstChild; |
| this.builder = null; |
| } |
| |
| public void setOperator(ExpressionOperator newOperator) { |
| operator = newOperator; |
| } |
| |
| protected void setSecondChild(Expression secondChild) { |
| this.secondChild = secondChild; |
| this.builder = null; |
| } |
| |
| /** |
| * INTRENAL: |
| * Used to change an expression off of one base to an expression off of a different base. |
| * i.e. expression on address to an expression on an employee's address. |
| */ |
| @Override |
| public Expression twistedForBaseAndContext(Expression newBase, Expression context, Expression oldBase) { |
| Vector arguments; |
| |
| if (this.secondChild == null) { |
| arguments = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(0); |
| } else { |
| arguments = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(1); |
| arguments.addElement(this.secondChild.twistedForBaseAndContext(newBase, context, oldBase)); |
| } |
| |
| Expression first = this.firstChild.twistedForBaseAndContext(newBase, context, oldBase); |
| return first.performOperator(this.operator, arguments); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used to print a debug form of the expression tree. |
| */ |
| @Override |
| public void writeDescriptionOn(BufferedWriter writer) throws IOException { |
| writer.write(operator.toString()); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for toString for debugging only. |
| */ |
| @Override |
| public void writeSubexpressionsTo(BufferedWriter writer, int indent) throws IOException { |
| if (this.firstChild != null) { |
| this.firstChild.toString(writer, indent); |
| } |
| if (this.secondChild != null) { |
| this.secondChild.toString(writer, indent); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Clear the builder when cloning. |
| */ |
| @Override |
| public Expression shallowClone() { |
| CompoundExpression clone = (CompoundExpression)super.shallowClone(); |
| clone.builder = null; |
| return clone; |
| } |
| } |