/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation 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; public CompoundExpression() { super(); } /** * INTERNAL: * Return if the expression is equal to the other. * This is used to allow dynamic expression's SQL to be cached. */ 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. */ 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 */ 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; } public Expression asOf(AsOfClause clause) { final AsOfClause finalClause = clause; ExpressionIterator iterator = new ExpressionIterator() { public void iterate(Expression each) { if (each.isDataExpression()) { each.asOf(finalClause); } } public boolean shouldIterateOverSubSelects() { return true; } }; iterator.iterateOn(this); return this; } /** * INTERNAL: */ 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. */ 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) */ 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; } 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()); } } public boolean isCompoundExpression() { return true; } /** * INTERNAL: * For iterating using an inner class */ 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. */ 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. */ 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. */ 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 */ 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 */ 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 */ 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. */ 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. */ public void writeDescriptionOn(BufferedWriter writer) throws IOException { writer.write(operator.toString()); } /** * INTERNAL: * Used for toString for debugging only. */ 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. */ public Expression shallowClone() { CompoundExpression clone = (CompoundExpression)super.shallowClone(); clone.builder = null; return clone; } }