blob: 74ed14c95bacdfc7d592904550a52b52b2180a2c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 1998, 2013 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.util.*;
import java.io.*;
import org.eclipse.persistence.exceptions.*;
import org.eclipse.persistence.mappings.*;
import org.eclipse.persistence.internal.helper.*;
import org.eclipse.persistence.expressions.*;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
/**
* Field expressions represent a field of a table.
* Their base is either a table or object expression.
* This is used for mapping queries and to allow queries on non-mapped field or tables.
*/
public class FieldExpression extends DataExpression {
protected DatabaseField field;
protected transient DatabaseField aliasedField;
/**
* FieldExpression constructor comment.
*/
public FieldExpression() {
super();
}
/**
* FieldExpression constructor comment.
*/
public FieldExpression(DatabaseField newField) {
super();
field = newField;
}
/**
* FieldExpression constructor comment.
*/
public FieldExpression(DatabaseField newField, Expression myBase) {
super();
field = newField;
baseExpression = myBase;
}
/**
* INTERNAL:
* Return if the expression is equal to the other.
* This is used to allow dynamic expression's SQL to be cached.
* This must be over written by each subclass.
*/
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!super.equals(object)) {
return false;
}
FieldExpression expression = (FieldExpression)object;
return ((getField() == expression.getField()) || ((getField() != null) && getField().equals(expression.getField())));
}
/**
* 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 (getField() != null) {
hashCode = hashCode + getField().hashCode();
}
return hashCode;
}
/**
* INTERNAL:
*/
public void clearAliases() {
super.clearAliases();
aliasedField = null;
}
/**
* INTERNAL:
* Used for debug printing.
*/
public String descriptionOfNodeType() {
return "Field";
}
/**
* INTERNAL:
* Return the field appropriately aliased
*/
public DatabaseField getAliasedField() {
if (aliasedField == null) {
initializeAliasedField();
}
return aliasedField;
}
/**
* Return the alias for our table
*/
private DatabaseTable getAliasedTable() {
DataExpression base = (DataExpression)getBaseExpression();
DatabaseField field = getField();
if (!field.hasTableName()) {
if (base.getDescriptor() != null){
field = base.getDescriptor().buildField(field);
}
}
DatabaseTable alias = base.aliasForTable(field.getTable());
if (alias == null) {
return field.getTable();
} else {
return alias;
}
}
/**
* INTERNAL:
* If there are any fields associated with this expression, return them
*/
public DatabaseField getClonedField() {
return getField().clone();
}
/**
* INTERNAL:
* If there are any fields associated with this expression, return them
*/
public Vector getClonedFields() {
Vector result = new Vector(1);
result.addElement(getField().clone());
return result;
}
/**
* INTERNAL:
*/
public DatabaseField getField() {
return field;
}
/**
* INTERNAL:
* Return all the fields
*/
public Vector getFields() {
Vector result = new Vector(1);
result.addElement(getField());
return result;
}
/**
* INTERNAL:
* Provide for conversion of the passed object's value, based on the referenced field value.
*/
@Override
public Object getFieldValue(Object value, AbstractSession session) {
// Bug 418705
if (getField() != null && value != null && (value.getClass() != getField().getType()) && !(value instanceof Collection) && !(value instanceof Enum)) {
try {
return session.getPlatform().convertObject(value, getField().getType());
} catch (ConversionException c) {}
}
return super.getFieldValue(value, session);
}
/**
* INTERNAL:
* Alias the database field for our current environment
*/
private void initializeAliasedField() {
DatabaseField tempField = getField().clone();
DatabaseTable aliasedTable = getAliasedTable();
// Put in a special check here so that if the aliasing does nothing we don't cache the
// result because it's invalid. This saves us from caching premature data if e.g. debugging
// causes us to print too early"
// if (aliasedTable.equals(getField().getTable())) {
// return;
// } else {
aliasedField = tempField;
aliasedField.setTable(aliasedTable);
// }
}
/**
* INTERNAL:
*/
public boolean isAttribute() {
return true;
}
public boolean isFieldExpression() {
return true;
}
/**
* INTERNAL:
* Normalize the expression into a printable structure.
*/
public Expression normalize(ExpressionNormalizer normalizer) {
if (this.hasBeenNormalized) {
return this;
}
Expression expression = super.normalize(normalizer);
// to support custom types, print expressions derived from field expressions, table expressions and direct query keys with their aliases
if (getBaseExpression() != null && getBaseExpression().isFieldExpression() || getBaseExpression().isTableExpression() ||
(getBaseExpression().isQueryKeyExpression() && ((QueryKeyExpression)getBaseExpression()).isAttribute())){
getBuilder().getStatement().setRequiresAliases(true);
}
return expression;
}
/**
* INTERNAL:
* Print SQL onto the stream, using the ExpressionPrinter for context
*/
public void printSQL(ExpressionSQLPrinter printer) {
// to support custom types, print expressions derived from field expressions and direct query keys with their aliases
// Note: This is also necessary for TableExpressions, but they are taken care of by their associated FieldExpression.
if (getBaseExpression() != null && getBaseExpression().isFieldExpression() ||
(getBaseExpression().isQueryKeyExpression() && ((QueryKeyExpression)getBaseExpression()).isAttribute())){
getBaseExpression().printSQL(printer);
printer.printString(".");
}
printer.printField(getAliasedField());
}
/**
* INTERNAL:
* Print java for project class generation
*/
public void printJava(ExpressionJavaPrinter printer) {
getBaseExpression().printJava(printer);
printer.printString(".getField(\"" + getField().getQualifiedName() + "\")");
}
/**
* 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) {
FieldExpression expression = new FieldExpression(getField(), getBaseExpression().rebuildOn(newBase));
expression.setSelectIfOrderedBy(selectIfOrderedBy());
return expression;
}
/**
* INTERNAL:
* Set the field in the mapping.
*/
public void setField(DatabaseField newField) {
field = newField;
}
/**
* 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.getField(getField());
}
/**
* Do any required validation for this node. Throw an exception if it's incorrect.
*/
public void validateNode() {
DataExpression base = (DataExpression)getBaseExpression();
if (getField().getTable().hasName()) {
List<DatabaseTable> tables = base.getOwnedTables();
if ((tables != null) && (!tables.contains((getField().getTable())))) {
throw QueryException.invalidTableForFieldInExpression(getField());
}
}
}
/**
* INTERNAL:
* Return the value for in memory comparison.
* This is only valid for valueable expressions.
*/
public Object valueFromObject(Object object, AbstractSession session, AbstractRecord translationRow, int valueHolderPolicy, boolean isObjectUnregistered) {
// Joins not supported.
if (!getBaseExpression().isExpressionBuilder()) {
throw QueryException.cannotConformExpression();
}
// For bug 2780817 get the mapping directly from the object. In EJB 2.0
// inheritance, each child must override mappings defined in an abstract
// class with its own.
DatabaseMapping mapping = session.getDescriptor(object.getClass()).getObjectBuilder().getMappingForField(getField());
if (mapping == null) {
throw QueryException.cannotConformExpression();
}
return mapping.valueFromObject(object, getField(), session);
}
/**
* INTERNAL:
* Used to print a debug form of the expression tree.
*/
public void writeDescriptionOn(BufferedWriter writer) throws IOException {
writer.write(getField().toString());
}
/**
* INTERNAL: called from SQLSelectStatement.writeFieldsFromExpression(...)
*/
@Override
public void writeFields(ExpressionSQLPrinter printer, Vector newFields, SQLSelectStatement statement) {
DatabaseField field = getField();
if (field != null) {
newFields.addElement(field);
writeField(printer, field, statement);
}
}
protected void writeField(ExpressionSQLPrinter printer, DatabaseField field, SQLSelectStatement statement) {
if (this.field == field){
//print ", " before each selected field except the first one
if (printer.isFirstElementPrinted()) {
printer.printString(", ");
} else {
printer.setIsFirstElementPrinted(true);
}
printSQL(printer);
} else {
super.writeField(printer, field, statement);
}
}
/**
* INTERNAL:
* writes the field for fine-grained pessimistic locking.
*/
protected void writeForUpdateOf(ExpressionSQLPrinter printer, SQLSelectStatement statement) {
if (printer.getPlatform().shouldPrintAliasForUpdate()) {
writeAlias(printer, field, statement);
} else {
writeField(printer, field, statement);
}
}
}