/******************************************************************************* | |
* 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); | |
} | |
} | |
} |