blob: 77e0e604bee0dc87b797375ada28cfaac9a5c010 [file] [log] [blame]
/*
* 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;
}
}