/******************************************************************************* * 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.expressions.*; import org.eclipse.persistence.internal.helper.DatabaseTable; import org.eclipse.persistence.internal.queries.ReportItem; /** * Allow a table expression to be created on a sub-select to define a sub-select in the from clause. */ public class FromSubSelectExpression extends TableExpression { /** Allows a sub-select to be defined from clause. */ protected SubSelectExpression subSelect; public FromSubSelectExpression() { super(); } public FromSubSelectExpression(SubSelectExpression subSelect) { super(); this.subSelect = subSelect; this.table = new SubSelectDatabaseTable(subSelect); } /** * INTERNAL: * Return if the expression is equal to the other. * This is used to allow dynamic expression's SQL to be cached. * From sub-selects are too complex to cache, so use identity. */ @Override public boolean equals(Object object) { return this == object; } /** * INTERNAL: * Compute a consistent hash-code for the expression. */ @Override public int computeHashCode() { return System.identityHashCode(this); } /** * INTERNAL: * Used for debug printing. */ @Override public String descriptionOfNodeType() { return "FromSubSelect"; } /** * This is used by sub-selects in the from clause to define a virtual table, * 'get' allows one of the sub-selected attributes to be aliased without using the field name. */ public Expression get(String alias) { FromAliasExpression aliasExpression = new FromAliasExpression(alias, this); return aliasExpression; } /** * INTERNAL: * Normalize the expression into a printable structure. * Any joins must be added to form a new root. */ @Override public Expression normalize(ExpressionNormalizer normalizer) { if (this.subSelect != null) { // Each item that is a function must have an alias defined for it. for (ReportItem item : this.subSelect.getSubQuery().getItems()) { if (!item.getAttributeExpression().isQueryKeyExpression()) { item.setAttributeExpression(item.getAttributeExpression().as(item.getName())); } } // Need to force the sub-slect to normalize instead of deferring. this.subSelect.normalizeSubSelect(normalizer, normalizer.getClonedExpressions()); } return super.normalize(normalizer); } /** * INTERNAL: * Also iterate over the sub-select if present. */ @Override public void iterateOn(ExpressionIterator iterator) { super.iterateOn(iterator); if (this.subSelect != null) { this.subSelect.iterateOn(iterator); } } /** * INTERNAL: * Also copy over the sub-select if present. */ @Override protected void postCopyIn(Map alreadyDone) { super.postCopyIn(alreadyDone); if (this.subSelect != null) { this.subSelect = (SubSelectExpression)this.subSelect.copiedVersionFrom(alreadyDone); this.table = new SubSelectDatabaseTable(subSelect); } } /** * 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) { Expression newLocalBase = getBaseExpression().rebuildOn(newBase); return newLocalBase.getAlias(this.subSelect.rebuildOn(newBase)); } /** * INTERNAL: * Rebuild myself against the base, with the values of parameters supplied by the context * expression. This is used for transforming a standalone expression (e.g. the join criteria of a mapping) * into part of some larger expression. You normally would not call this directly, instead calling twist * See the comment there for more details" */ @Override public Expression twistedForBaseAndContext(Expression newBase, Expression context, Expression oldBase) { Expression twistedBase = getBaseExpression().twistedForBaseAndContext(newBase, context, oldBase); return twistedBase.getAlias(this.subSelect.twistedForBaseAndContext(newBase, context, oldBase)); } /** * INTERNAL: * Used to print a debug form of the expression tree. */ @Override public void writeDescriptionOn(BufferedWriter writer) throws IOException { this.subSelect.writeDescriptionOn(writer); writer.write(tableAliasesDescription()); } /** * INTERNAL: * Any table should use the sub-selects alias. */ @Override public DatabaseTable aliasForTable(DatabaseTable table) { return super.aliasForTable(getTable()); } public SubSelectExpression getSubSelect() { return subSelect; } public void setSubSelect(SubSelectExpression subSelect) { this.subSelect = subSelect; this.table = new SubSelectDatabaseTable(subSelect); } }