gt: 			type = getPropertyType();
context: /*
   * Hibernate, Relational Persistence for Idiomatic Java
   *
   * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
   * indicated by the @author tags or express copyright attribution
   * statements applied by the authors.  All third-party contributions are
   * distributed under license by Red Hat Middleware LLC.
   *
   * This copyrighted material is made available to anyone wishing to use, modify,
   * copy, or redistribute it subject to the terms and conditions of the GNU
   * Lesser General Public License, as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
   * for more details.
   *
   * You should have received a copy of the GNU Lesser General Public License
   * along with this distribution; if not, write to:
   * Free Software Foundation, Inc.
   * 51 Franklin Street, Fifth Floor
   * Boston, MA  02110-1301  USA
   *
   */
  package org.hibernate.hql.internal.classic;
  import java.util.LinkedList;
  import java.util.Map;
  
  import org.hibernate.MappingException;
  import org.hibernate.QueryException;
  import org.hibernate.engine.internal.JoinSequence;
  import org.hibernate.hql.internal.CollectionSubqueryFactory;
  import org.hibernate.persister.collection.CollectionPropertyMapping;
  import org.hibernate.persister.collection.QueryableCollection;
  import org.hibernate.persister.entity.EntityPersister;
  import org.hibernate.persister.entity.PropertyMapping;
  import org.hibernate.persister.entity.Queryable;
  import org.hibernate.sql.JoinType;
  import org.hibernate.type.AssociationType;
  import org.hibernate.type.CollectionType;
  import org.hibernate.type.EntityType;
  import org.hibernate.type.Type;
  
  /**
   * Parses an expression of the form foo.bar.baz and builds up an expression
   * involving two less table joins than there are path components.
   */
  public class PathExpressionParser implements Parser {
  
  	//TODO: this class does too many things! we need a different
  	//kind of path expression parser for each of the diffferent
  	//ways in which path expressions can occur
  
  	//We should actually rework this class to not implement Parser
  	//and just process path expressions in the most convenient way.
  
  	//The class is now way to complex!
  
  	private int dotcount;
  	private String currentName;
  	private String currentProperty;
  	private String oneToOneOwnerName;
  	private AssociationType ownerAssociationType;
  	private String[] columns;
  	private String collectionName;
  	private String collectionOwnerName;
  	private String collectionRole;
  	private final StringBuilder componentPath = new StringBuilder();
  	private Type type;
  	private final StringBuilder path = new StringBuilder();
  	private boolean ignoreInitialJoin;
  	private boolean continuation;
  	private JoinType joinType = JoinType.INNER_JOIN; //default mode
  	private boolean useThetaStyleJoin = true;
  	private PropertyMapping currentPropertyMapping;
  	private JoinSequence joinSequence;
  
  	private boolean expectingCollectionIndex;
  	private LinkedList collectionElements = new LinkedList();
  
  	void setJoinType(JoinType joinType) {
  		this.joinType = joinType;
  	}
  
  	void setUseThetaStyleJoin(boolean useThetaStyleJoin) {
  		this.useThetaStyleJoin = useThetaStyleJoin;
  	}
  
  	private void addJoin(String name, AssociationType joinableType) throws QueryException {
  		try {
  			joinSequence.addJoin( joinableType, name, joinType, currentColumns() );
  		}
  		catch ( MappingException me ) {
  			throw new QueryException( me );
  		}
  	}
  
  	private void addJoin(String name, AssociationType joinableType, String[] foreignKeyColumns) throws QueryException {
  		try {
  			joinSequence.addJoin( joinableType, name, joinType, foreignKeyColumns );
  		}
  		catch ( MappingException me ) {
  			throw new QueryException( me );
  		}
  	}
  
  	String continueFromManyToMany(String entityName, String[] joinColumns, QueryTranslatorImpl q) throws QueryException {
  		start( q );
  		continuation = true;
  		currentName = q.createNameFor( entityName );
  		q.addType( currentName, entityName );
  		Queryable classPersister = q.getEntityPersister( entityName );
  		//QueryJoinFragment join = q.createJoinFragment(useThetaStyleJoin);
  		addJoin( currentName, q.getFactory().getTypeResolver().getTypeFactory().manyToOne( entityName ), joinColumns );
  		currentPropertyMapping = classPersister;
  		return currentName;
  	}
  
  	public void ignoreInitialJoin() {
  		ignoreInitialJoin = true;
  	}
  
  	public void token(String token, QueryTranslatorImpl q) throws QueryException {
  
  		if ( token != null ) path.append( token );
  
  		String alias = q.getPathAlias( path.toString() );
  		if ( alias != null ) {
  			reset( q ); //reset the dotcount (but not the path)
  			currentName = alias; //after reset!
  			currentPropertyMapping = q.getPropertyMapping( currentName );
  			if ( !ignoreInitialJoin ) {
  				JoinSequence ojf = q.getPathJoin( path.toString() );
  				try {
  					joinSequence.addCondition( ojf.toJoinFragment( q.getEnabledFilters(), true ).toWhereFragmentString() ); //after reset!
  				}
  				catch ( MappingException me ) {
  					throw new QueryException( me );
  				}
  				// we don't need to worry about any condition in the ON clause
  				// here (toFromFragmentString), since anything in the ON condition
  				// is already applied to the whole query
  			}
  		}
  		else if ( ".".equals( token ) ) {
  			dotcount++;
  		}
  		else {
  			if ( dotcount == 0 ) {
  				if ( !continuation ) {
  					if ( !q.isName( token ) ) throw new QueryException( "undefined alias: " + token );
  					currentName = token;
  					currentPropertyMapping = q.getPropertyMapping( currentName );
  				}
  			}
  			else if ( dotcount == 1 ) {
  				if ( currentName != null ) {
  					currentProperty = token;
  				}
  				else if ( collectionName != null ) {
  					//processCollectionProperty(token, q.getCollectionPersister(collectionRole), collectionName);
  					continuation = false;
  				}
  				else {
  					throw new QueryException( "unexpected" );
  				}
  			}
  			else { // dotcount>=2
  
  				// Do the corresponding RHS
  				Type propertyType = getPropertyType();
  
  				if ( propertyType == null ) {
  					throw new QueryException( "unresolved property: " + path );
  				}
  
  				if ( propertyType.isComponentType() ) {
  					dereferenceComponent( token );
  				}
  				else if ( propertyType.isEntityType() ) {
  					if ( !isCollectionValued() ) dereferenceEntity( token, ( EntityType ) propertyType, q );
  				}
  				else if ( propertyType.isCollectionType() ) {
  					dereferenceCollection( token, ( ( CollectionType ) propertyType ).getRole(), q );
  
  				}
  				else {
  					if ( token != null ) throw new QueryException( "dereferenced: " + path );
  				}
  
  			}
  		}
  	}
  
  	private void dereferenceEntity(String propertyName, EntityType propertyType, QueryTranslatorImpl q)
  			throws QueryException {
  		//NOTE: we avoid joining to the next table if the named property is just the foreign key value
  
  		//if its "id"
  		boolean isIdShortcut = EntityPersister.ENTITY_ID.equals( propertyName ) &&
  				propertyType.isReferenceToPrimaryKey();
  
  		//or its the id property name
  		final String idPropertyName;
  		try {
  			idPropertyName = propertyType.getIdentifierOrUniqueKeyPropertyName( q.getFactory() );
  		}
  		catch ( MappingException me ) {
  			throw new QueryException( me );
  		}
  		boolean isNamedIdPropertyShortcut = idPropertyName != null
  				&& idPropertyName.equals( propertyName )
  				&& propertyType.isReferenceToPrimaryKey();
  
  
  		if ( isIdShortcut || isNamedIdPropertyShortcut ) {
  			// special shortcut for id properties, skip the join!
  			// this must only occur at the _end_ of a path expression
  			if ( componentPath.length() > 0 ) componentPath.append( '.' );
  			componentPath.append( propertyName );
  		}
  		else {
  			String entityClass = propertyType.getAssociatedEntityName();
  			String name = q.createNameFor( entityClass );
  			q.addType( name, entityClass );
  			addJoin( name, propertyType );
  			if ( propertyType.isOneToOne() ) oneToOneOwnerName = currentName;
  			ownerAssociationType = propertyType;
  			currentName = name;
  			currentProperty = propertyName;
  			q.addPathAliasAndJoin( path.substring( 0, path.toString().lastIndexOf( '.' ) ), name, joinSequence.copy() );
  			componentPath.setLength( 0 );
  			currentPropertyMapping = q.getEntityPersister( entityClass );
  		}
  	}
  
  	private void dereferenceComponent(String propertyName) {
  		if ( propertyName != null ) {
  			if ( componentPath.length() > 0 ) componentPath.append( '.' );
  			componentPath.append( propertyName );
  		}
  	}
  
  	private void dereferenceCollection(String propertyName, String role, QueryTranslatorImpl q) throws QueryException {
  		collectionRole = role;
  		QueryableCollection collPersister = q.getCollectionPersister( role );
  		String name = q.createNameForCollection( role );
  		addJoin( name, collPersister.getCollectionType() );
  		//if ( collPersister.hasWhere() ) join.addCondition( collPersister.getSQLWhereString(name) );
  		collectionName = name;
  		collectionOwnerName = currentName;
  		currentName = name;
  		currentProperty = propertyName;
  		componentPath.setLength( 0 );
  		currentPropertyMapping = new CollectionPropertyMapping( collPersister );
  	}
  
  	private String getPropertyPath() {
  		if ( currentProperty == null ) {
  			return EntityPersister.ENTITY_ID;
  		}
  		else {
  			if ( componentPath.length() > 0 ) {
  				return new StringBuilder()
  						.append( currentProperty )
  						.append( '.' )
  						.append( componentPath.toString() )
  						.toString();
  			}
  			else {
  				return currentProperty;
  			}
  		}
  	}
  
  	private PropertyMapping getPropertyMapping() {
  		return currentPropertyMapping;
  	}
  
  	private void setType() throws QueryException {
  		if ( currentProperty == null ) {
  			type = getPropertyMapping().getType();
  		}
  		else {
  