org.hibernate.hql.ast.tree.IdentNode Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate Show documentation
Show all versions of hibernate Show documentation
Relational Persistence for Java
// $Id: IdentNode.java 10059 2006-06-28 02:37:56Z [email protected] $
package org.hibernate.hql.ast.tree;
import antlr.SemanticException;
import antlr.collections.AST;
import org.hibernate.QueryException;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.hql.antlr.SqlTokenTypes;
import org.hibernate.hql.ast.util.ColumnHelper;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.JoinFragment;
import org.hibernate.type.CollectionType;
import org.hibernate.type.Type;
import org.hibernate.util.StringHelper;
import java.util.List;
/**
* Represents an identifier all by itself, which may be a function name,
* a class alias, or a form of naked property-ref depending on the
* context.
*
* @author josh Aug 16, 2004 7:20:55 AM
*/
public class IdentNode extends FromReferenceNode implements SelectExpression {
private static final int UNKNOWN = 0;
private static final int PROPERTY_REF = 1;
private static final int COMPONENT_REF = 2;
private boolean nakedPropertyRef = false;
public void resolveIndex(AST parent) throws SemanticException {
// An ident node can represent an index expression if the ident
// represents a naked property ref
// *Note: this makes the assumption (which is currently the case
// in the hql-sql grammar) that the ident is first resolved
// itself (addrExpr -> resolve()). The other option, if that
// changes, is to call resolve from here; but it is
// currently un-needed overhead.
if (!(isResolved() && nakedPropertyRef)) {
throw new UnsupportedOperationException();
}
String propertyName = getOriginalText();
if (!getDataType().isCollectionType()) {
throw new SemanticException("Collection expected; [" + propertyName + "] does not refer to a collection property");
}
// TODO : most of below was taken verbatim from DotNode; should either delegate this logic or super-type it
CollectionType type = (CollectionType) getDataType();
String role = type.getRole();
QueryableCollection queryableCollection = getSessionFactoryHelper().requireQueryableCollection(role);
String alias = null; // DotNode uses null here...
String columnTableAlias = getFromElement().getTableAlias();
int joinType = JoinFragment.INNER_JOIN;
boolean fetch = false;
FromElementFactory factory = new FromElementFactory(
getWalker().getCurrentFromClause(),
getFromElement(),
propertyName,
alias,
getFromElement().toColumns(columnTableAlias, propertyName, false),
true
);
FromElement elem = factory.createCollection(queryableCollection, role, joinType, fetch, true);
setFromElement(elem);
getWalker().addQuerySpaces(queryableCollection.getCollectionSpaces()); // Always add the collection's query spaces.
}
public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent) {
if (!isResolved()) {
if (getWalker().getCurrentFromClause().isFromElementAlias(getText())) {
if (resolveAsAlias()) {
setResolved();
// We represent a from-clause alias
}
}
else if (parent != null && parent.getType() == SqlTokenTypes.DOT) {
DotNode dot = (DotNode) parent;
if (parent.getFirstChild() == this) {
if (resolveAsNakedComponentPropertyRefLHS(dot)) {
// we are the LHS of the DOT representing a naked comp-prop-ref
setResolved();
}
}
else {
if (resolveAsNakedComponentPropertyRefRHS(dot)) {
// we are the RHS of the DOT representing a naked comp-prop-ref
setResolved();
}
}
}
else {
int result = resolveAsNakedPropertyRef();
if (result == PROPERTY_REF) {
// we represent a naked (simple) prop-ref
setResolved();
}
else if (result == COMPONENT_REF) {
// EARLY EXIT!!! return so the resolve call explicitly coming from DotNode can
// resolve this...
return;
}
}
// if we are still not resolved, we might represent a constant.
// needed to add this here because the allowance of
// naked-prop-refs in the grammar collides with the
// definition of literals/constants ("nondeterminism").
// TODO: cleanup the grammar so that "processConstants" is always just handled from here
if (!isResolved()) {
try {
getWalker().getLiteralProcessor().processConstant(this, false);
}
catch (Throwable ignore) {
// just ignore it for now, it'll get resolved later...
}
}
}
}
private boolean resolveAsAlias() {
// This is not actually a constant, but a reference to FROM element.
FromElement element = getWalker().getCurrentFromClause().getFromElement(getText());
if (element != null) {
setFromElement(element);
setText(element.getIdentityColumn());
setType(SqlTokenTypes.ALIAS_REF);
return true;
}
return false;
}
private Type getNakedPropertyType(FromElement fromElement)
{
if (fromElement == null) {
return null;
}
String property = getOriginalText();
Type propertyType = null;
try {
propertyType = fromElement.getPropertyType(property, property);
}
catch (Throwable t) {
}
return propertyType;
}
private int resolveAsNakedPropertyRef() {
FromElement fromElement = locateSingleFromElement();
if (fromElement == null) {
return UNKNOWN;
}
Queryable persister = fromElement.getQueryable();
if (persister == null) {
return UNKNOWN;
}
Type propertyType = getNakedPropertyType(fromElement);
if (propertyType == null) {
// assume this ident's text does *not* refer to a property on the given persister
return UNKNOWN;
}
if ((propertyType.isComponentType() || propertyType.isAssociationType() )) {
return COMPONENT_REF;
}
setFromElement(fromElement);
String property = getText();
String[] columns = getWalker().isSelectStatement()
? persister.toColumns(fromElement.getTableAlias(), property)
: persister.toColumns(property);
String text = StringHelper.join(", ", columns);
setText(columns.length == 1 ? text : "(" + text + ")");
setType(SqlTokenTypes.SQL_TOKEN);
// these pieces are needed for usage in select clause
super.setDataType(propertyType);
nakedPropertyRef = true;
return PROPERTY_REF;
}
private boolean resolveAsNakedComponentPropertyRefLHS(DotNode parent) {
FromElement fromElement = locateSingleFromElement();
if (fromElement == null) {
return false;
}
Type componentType = getNakedPropertyType(fromElement);
if ( componentType == null ) {
throw new QueryException( "Unable to resolve path [" + parent.getPath() + "], unexpected token [" + getOriginalText() + "]" );
}
if (!componentType.isComponentType()) {
throw new QueryException("Property '" + getOriginalText() + "' is not a component. Use an alias to reference associations or collections.");
}
Type propertyType = null; // used to set the type of the parent dot node
String propertyPath = getText() + "." + getNextSibling().getText();
try {
// check to see if our "propPath" actually
// represents a property on the persister
propertyType = fromElement.getPropertyType(getText(), propertyPath);
}
catch (Throwable t) {
// assume we do *not* refer to a property on the given persister
return false;
}
setFromElement(fromElement);
parent.setPropertyPath(propertyPath);
parent.setDataType(propertyType);
return true;
}
private boolean resolveAsNakedComponentPropertyRefRHS(DotNode parent) {
FromElement fromElement = locateSingleFromElement();
if (fromElement == null) {
return false;
}
Type propertyType = null;
String propertyPath = parent.getLhs().getText() + "." + getText();
try {
// check to see if our "propPath" actually
// represents a property on the persister
propertyType = fromElement.getPropertyType(getText(), propertyPath);
}
catch (Throwable t) {
// assume we do *not* refer to a property on the given persister
return false;
}
setFromElement(fromElement);
// this piece is needed for usage in select clause
super.setDataType(propertyType);
nakedPropertyRef = true;
return true;
}
private FromElement locateSingleFromElement() {
List fromElements = getWalker().getCurrentFromClause().getFromElements();
if (fromElements == null || fromElements.size() != 1) {
// TODO : should this be an error?
return null;
}
FromElement element = (FromElement) fromElements.get(0);
if (element.getClassAlias() != null) {
// naked property-refs cannot be used with an aliased from element
return null;
}
return element;
}
public Type getDataType() {
Type type = super.getDataType();
if (type != null) return type;
FromElement fe = getFromElement();
if (fe != null) return fe.getDataType();
SQLFunction sf = getWalker().getSessionFactoryHelper().findSQLFunction(getText());
return sf == null ? null : sf.getReturnType(null, null);
}
public void setScalarColumnText(int i) throws SemanticException {
if (nakedPropertyRef) {
// do *not* over-write the column text, as that has already been
// "rendered" during resolve
ColumnHelper.generateSingleScalarColumn(this, i);
}
else {
FromElement fe = getFromElement();
if (fe != null) {
setText(fe.renderScalarIdentifierSelect(i));
}
else {
ColumnHelper.generateSingleScalarColumn(this, i);
}
}
}
public String getDisplayText() {
StringBuffer buf = new StringBuffer();
if (getType() == SqlTokenTypes.ALIAS_REF) {
buf.append("{alias=").append(getOriginalText());
if (getFromElement() == null) {
buf.append(", no from element");
}
else {
buf.append(", className=").append(getFromElement().getClassName());
buf.append(", tableAlias=").append(getFromElement().getTableAlias());
}
buf.append("}");
}
else {
buf.append("{originalText=" + getOriginalText()).append("}");
}
return buf.toString();
}
}