/******************************************************************************* | |
* 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: | |
* May 21, 2009-2.0 Chris Delahunt | |
* - TODO Bug#: Bug Description | |
******************************************************************************/ | |
package org.eclipse.persistence.internal.expressions; | |
import java.io.BufferedWriter; | |
import java.io.IOException; | |
import java.util.Collection; | |
import java.util.Enumeration; | |
import java.util.Iterator; | |
import java.util.Vector; | |
import org.eclipse.persistence.exceptions.QueryException; | |
import org.eclipse.persistence.expressions.Expression; | |
import org.eclipse.persistence.expressions.ExpressionBuilder; | |
import org.eclipse.persistence.internal.helper.DatabaseField; | |
import org.eclipse.persistence.internal.helper.DatabaseTable; | |
import org.eclipse.persistence.internal.sessions.AbstractRecord; | |
import org.eclipse.persistence.internal.sessions.AbstractSession; | |
import org.eclipse.persistence.queries.ObjectLevelReadQuery; | |
import org.eclipse.persistence.descriptors.ClassDescriptor; | |
/** | |
* @author cdelahun | |
* | |
*/ | |
public class ClassTypeExpression extends DataExpression { | |
/** Cache the aliased field. Only applies to attributes. */ | |
protected DatabaseField field; | |
/** Cache the aliased field. Only applies to attributes. */ | |
protected DatabaseField aliasedField; | |
/** | |
* | |
*/ | |
public ClassTypeExpression(Expression base) { | |
super(); | |
this.baseExpression = base; | |
} | |
public ClassTypeExpression() { | |
super(); | |
} | |
/** | |
* INTERNAL: | |
* Used for debug printing. | |
*/ | |
public String descriptionOfNodeType() { | |
return "Class For Inheritance"; | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.persistence.expressions.Expression#rebuildOn(org.eclipse.persistence.expressions.Expression) | |
*/ | |
@Override | |
public Expression rebuildOn(Expression newBase) { | |
Expression newLocalBase = getBaseExpression().rebuildOn(newBase); | |
Expression result = newLocalBase.type(); | |
result.setSelectIfOrderedBy(selectIfOrderedBy()); | |
return result; | |
} | |
/** | |
* INTERNAL | |
* This method returns the inheritance field value for an object to conform in an in-memory query. | |
* Similar to getFieldValue, but deals with an instance rather than a Class object directly | |
*/ | |
public Object typeValueFromObject(Object object, AbstractSession session) { | |
// get the descriptor directly from the object, and use it to find the Java class | |
ClassDescriptor objectDescriptor = session.getClassDescriptor(object); | |
if (!objectDescriptor.hasInheritance() | |
|| objectDescriptor.getInheritancePolicy().shouldUseClassNameAsIndicator() | |
|| objectDescriptor.getInheritancePolicy().hasClassExtractor() ) { | |
return (objectDescriptor.getJavaClassName()); | |
} else { | |
return objectDescriptor.getInheritancePolicy().getClassIndicatorMapping().get(objectDescriptor.getJavaClass()); | |
} | |
} | |
public void validateNode() { | |
ClassDescriptor descriptor = getContainingDescriptor(); | |
if (descriptor ==null){ | |
throw QueryException.invalidTypeExpression(getBaseExpression()); | |
} | |
if ( (!descriptor.hasInheritance()) || (!descriptor.getInheritancePolicy().hasClassIndicator()) ) { | |
throw QueryException.invalidTypeExpression(descriptor.getJavaClassName()); | |
} | |
super.validateNode(); | |
} | |
/** | |
* INTERNAL: | |
* Return the value for in memory comparison. | |
* This is only valid for value expressions. | |
* Pulled from QueryKeyExpression valueFromObject | |
*/ | |
public Object valueFromObject(Object object, AbstractSession session, AbstractRecord translationRow, int valueHolderPolicy, boolean isObjectUnregistered) { | |
// The expression may be across a relationship, in which case it must be traversed. | |
if ((!getBaseExpression().isExpressionBuilder()) && getBaseExpression().isQueryKeyExpression()) { | |
object = getBaseExpression().valueFromObject(object, session, translationRow, valueHolderPolicy, isObjectUnregistered); | |
// toDo: Null means the join filters out the row, returning null is not correct if an inner join, | |
// outer/inner joins need to be fixed to filter correctly. | |
if (object == null) { | |
return null; | |
} | |
// If from an anyof the object will be a collection of values, | |
// A new vector must union the object values and the values extracted from it. | |
if (object instanceof Vector) { | |
Vector comparisonVector = new Vector(((Vector)object).size() + 2); | |
for (Enumeration valuesToIterate = ((Vector)object).elements(); | |
valuesToIterate.hasMoreElements();) { | |
Object vectorObject = valuesToIterate.nextElement(); | |
if (vectorObject == null) { | |
comparisonVector.addElement(vectorObject); | |
} else { | |
Object valueOrValues = typeValueFromObject(vectorObject, session); | |
// If a collection of values were extracted union them. | |
if (valueOrValues instanceof Vector) { | |
for (Enumeration nestedValuesToIterate = ((Vector)valueOrValues).elements(); | |
nestedValuesToIterate.hasMoreElements();) { | |
comparisonVector.addElement(nestedValuesToIterate.nextElement()); | |
} | |
} else { | |
comparisonVector.addElement(valueOrValues); | |
} | |
} | |
} | |
return comparisonVector; | |
} | |
} | |
return typeValueFromObject(object, session); | |
} | |
/** | |
* INTERNAL: | |
* Used to print a debug form of the expression tree. | |
*/ | |
public void writeDescriptionOn(BufferedWriter writer) throws IOException { | |
writer.write("TYPE"); | |
writer.write(tableAliasesDescription()); | |
} | |
/** | |
* INTERNAL: | |
*/ | |
public DatabaseField getField() { | |
if (field == null) { | |
ClassDescriptor descriptor = getContainingDescriptor(); | |
if (!descriptor.hasInheritance() || descriptor.getInheritancePolicy().hasClassExtractor()){ | |
throw QueryException.invalidTypeExpression(descriptor.getJavaClassName()); | |
} | |
field = descriptor.getInheritancePolicy().getClassIndicatorField(); | |
} | |
return field; | |
} | |
/** | |
* INTERNAL: | |
* Transform the object-level value into a database-level value | |
* objectValue is a Class or collection of Class objects and the returned value is the database representation | |
* Example: ObjectValue=LargeProject returns "L". | |
*/ | |
public Object getFieldValue(Object objectValue, AbstractSession session) { | |
if (objectValue ==null){ | |
return null; | |
} | |
if (objectValue instanceof Collection) { | |
// This can actually be a collection for IN within expressions... however it would be better for expressions to handle this. | |
Collection values = (Collection)objectValue; | |
Vector fieldValues = new Vector(values.size()); | |
for (Iterator iterator = values.iterator(); iterator.hasNext();) { | |
Object value = iterator.next(); | |
if (!(value instanceof Expression)){ | |
value = getFieldValue(value, session); | |
} | |
fieldValues.add(value); | |
} | |
return fieldValues; | |
} else { | |
if (! (objectValue instanceof Class) ){ | |
throw QueryException.invalidTypeExpression(objectValue.getClass().toString()); | |
} | |
ClassDescriptor descriptor = session.getDescriptor((Class)objectValue); | |
if (descriptor == null){ | |
throw QueryException.invalidTypeExpression(objectValue.getClass().toString()); | |
} | |
if (descriptor.hasInheritance() && !descriptor.getInheritancePolicy().shouldUseClassNameAsIndicator()){ | |
return descriptor.getInheritancePolicy().getClassIndicatorMapping().get(objectValue); | |
} else { | |
return ((Class)objectValue).getName(); | |
} | |
} | |
} | |
/** | |
* INTERNAL: | |
* Like QueryKeyExpression, return the descriptor for the class type used, null if one can't be determined yet. | |
* Should only be called when a session is already set. | |
*/ | |
public ClassDescriptor getContainingDescriptor() { | |
return ((ObjectExpression)getBaseExpression()).getDescriptor(); | |
} | |
/** | |
* INTERNAL: | |
* Return the descriptor for the base expression. This is used in ReportItem when building the | |
* return value (a class), as none of the expressions will have a session. | |
*/ | |
public ClassDescriptor getContainingDescriptor(ObjectLevelReadQuery query) { | |
Class queryClass = null; | |
if (getBaseExpression().isExpressionBuilder()){ | |
queryClass = ((ExpressionBuilder)getBaseExpression()).getQueryClass(); | |
return query.getSession().getDescriptor(queryClass); | |
} else { | |
// It must be a QueryKeyExpression. | |
return getBaseExpression().getLeafDescriptor(query, query.getDescriptor(), query.getSession()); | |
} | |
} | |
public boolean isClassTypeExpression(){ | |
return true; | |
} | |
/** | |
* INTERNAL: | |
*/ | |
public boolean isAttribute() { | |
return true; | |
} | |
/** | |
* INTERNAL: | |
* For CR#2456 if this is part of an objExp.equal(objExp), do not need to add | |
* additional expressions to normalizer both times, and the foreign key join | |
* replaces the equal expression. | |
*/ | |
public Expression normalize(ExpressionNormalizer normalizer, Vector foreignKeyJoinPointer) { | |
if (hasBeenNormalized()) { | |
return this; | |
} | |
return super.normalize(normalizer); | |
} | |
/** | |
* INTERNAL: | |
* Alias the database field for our current environment | |
*/ | |
protected void initializeAliasedField() { | |
DatabaseField tempField = getField().clone(); | |
DatabaseTable aliasedTable = getAliasedTable(); | |
aliasedField = tempField; | |
aliasedField.setTable(aliasedTable); | |
} | |
/** | |
* INTERNAL: | |
* Return the field appropriately aliased | |
*/ | |
public DatabaseField getAliasedField() { | |
if (aliasedField == null) { | |
initializeAliasedField(); | |
} | |
return aliasedField; | |
} | |
/** | |
* Return the alias for our table | |
*/ | |
protected DatabaseTable getAliasedTable() { | |
DataExpression base = (DataExpression)getBaseExpression(); | |
DatabaseTable alias = base.aliasForTable(getField().getTable()); | |
if (alias == null) { | |
return getField().getTable(); | |
} else { | |
return alias; | |
} | |
} | |
/** | |
* INTERNAL: | |
* Return all the fields | |
*/ | |
public Vector getFields() { | |
Vector result = new Vector(1); | |
DatabaseField field = getField(); | |
if (field != null) { | |
result.addElement(field); | |
} | |
return result; | |
} | |
@Override | |
public Expression twistedForBaseAndContext(Expression newBase, Expression context, Expression oldBase) { | |
if (oldBase == null || this.baseExpression == oldBase) { | |
Expression twistedBase = this.baseExpression.twistedForBaseAndContext(newBase, context, oldBase); | |
return twistedBase.type(); | |
} | |
return this; | |
} | |
} |