You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ojb-user@db.apache.org by Phil Warrick <ph...@mcgill.ca> on 2004/03/29 06:04:37 UTC

Re: Per-criteria pathClass [was ojb-user: Query casts] 2 of 2

Hi Jakob,

I applied my changes to 1.70 and am sending you the entire java file.  I
created a patch file with eclipse, but it did not seem to want to do an
intelligent diff (just -entire old file contents and +entire new file
contents).  There was no improvement if I turned off ignore whitespace
in the compare preferences.  Maybe there's some eclipse trick I need to
know here.

Anyway, hopefully now you have all my work related to this change.

Looking forward to your feedback,

Phil


Re: Per-criteria pathClass [was ojb-user: Query casts] 2 of 2

Posted by Jakob Braeuchi <jb...@gmx.ch>.
hi phil,

thanks for the file.
generating a patch in eclipse is quite easy (in most cases). seems you 
reformatted the code in some way, so the diff get's out of sync.

i'll have a look at your changes when i return from my vacation, that's 19. april.

jakob

Phil Warrick wrote:
> Hi Jakob,
> 
> I applied my changes to 1.70 and am sending you the entire java file.  I
> created a patch file with eclipse, but it did not seem to want to do an
> intelligent diff (just -entire old file contents and +entire new file
> contents).  There was no improvement if I turned off ignore whitespace
> in the compare preferences.  Maybe there's some eclipse trick I need to
> know here.
> 
> Anyway, hopefully now you have all my work related to this change.
> 
> Looking forward to your feedback,
> 
> Phil
> 
> 
> ------------------------------------------------------------------------
> 
> package org.apache.ojb.broker.accesslayer.sql;
> 
> /* Copyright  2004 The Apache Software Foundation
>  *
>  * Licensed under the Apache License, Version 2.0 (the "License");
>  * you may not use this file except in compliance with the License.
>  * You may obtain a copy of the License at
>  *
>  *     http://www.apache.org/licenses/LICENSE-2.0
>  *
>  * Unless required by applicable law or agreed to in writing, software
>  * distributed under the License is distributed on an "AS IS" BASIS,
>  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
>  * See the License for the specific language governing permissions and
>  * limitations under the License.
>  */
> 
> import java.util.ArrayList;
> import java.util.Collection;
> import java.util.Enumeration;
> import java.util.HashMap;
> import java.util.Iterator;
> import java.util.List;
> import java.util.Map;
> 
> import org.apache.ojb.broker.PersistenceBrokerSQLException;
> import org.apache.ojb.broker.accesslayer.JoinSyntaxTypes;
> import org.apache.ojb.broker.metadata.ClassDescriptor;
> import org.apache.ojb.broker.metadata.CollectionDescriptor;
> import org.apache.ojb.broker.metadata.DescriptorRepository;
> import org.apache.ojb.broker.metadata.FieldDescriptor;
> import org.apache.ojb.broker.metadata.FieldHelper;
> import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
> import org.apache.ojb.broker.metadata.fieldaccess.AnonymousPersistentFieldForInheritance;
> import org.apache.ojb.broker.platforms.Platform;
> import org.apache.ojb.broker.query.*;
> import org.apache.ojb.broker.util.SqlHelper;
> import org.apache.ojb.broker.util.SqlHelper.PathInfo;
> import org.apache.ojb.broker.util.logging.Logger;
> import org.apache.ojb.broker.util.logging.LoggerFactory;
> 
> /**
>  * Model a Statement based on Query.
>  *
>  * @author <a href="mailto:jbraeuchi@gmx.ch">Jakob Braeuchi</a>
>  * @version $Id: SqlQueryStatement.java,v 1.68 2004/03/11 18:16:08 brianm Exp $
>  */
> public abstract class SqlQueryStatement implements SqlStatement, JoinSyntaxTypes
> {
> 	private SqlQueryStatement m_parentStatement;
> 	/** the logger */
> 	private Logger m_logger;
> 	/** the target table of the query */
> 	private TableAlias m_root;
> 	/** the search table of the query */
> 	private TableAlias m_search;
> 	/** the query */
> 	private QueryByCriteria m_query;
> 	/** the mapping of paths to TableAliases */
> 	private HashMap m_pathToAlias = new HashMap();
> 	/** maps trees of joins to criteria */
> 	private HashMap m_joinTreeToCriteria = new HashMap();
> 
> 	private Platform m_platform;
> 	private ClassDescriptor m_baseCld;
> 	private ClassDescriptor m_searchCld;
> 
> 	private int m_aliasCount = 0;
> 
> 	/**
> 	 * Constructor for SqlCriteriaStatement.
> 	 *
> 	 * @param pf the Platform
> 	 * @param cld the ClassDescriptor
> 	 * @param query the Query
> 	 * @param logger the Logger
> 	 */
> 	public SqlQueryStatement(Platform pf, ClassDescriptor cld, Query query, Logger logger)
> 	{
> 		this(null, pf, cld, query, logger);
> 	}
> 
> 	/**
> 	 * Constructor for SqlCriteriaStatement.
> 	 *
> 	 * @param parent the Parent Query
> 	 * @param pf the Platform
> 	 * @param cld the ClassDescriptor
> 	 * @param query the Query
> 	 * @param logger the Logger
> 	 */
> 	public SqlQueryStatement(SqlQueryStatement parent, Platform pf, ClassDescriptor cld, Query query, Logger logger)
> 	{
> 		m_logger = logger != null ? logger : LoggerFactory.getLogger(SqlQueryStatement.class);
> 		m_parentStatement = parent;
> 		m_query = (QueryByCriteria) query;
> 		m_platform = pf;
> 		m_searchCld = cld;
> 
> 		if ((m_query == null) || (m_query.getBaseClass() == m_query.getSearchClass()))
> 		{
> 			m_baseCld = m_searchCld;
> 		}
> 		else
> 		{
> 			m_baseCld = cld.getRepository().getDescriptorFor(query.getBaseClass());
> 		}
> 
> 		m_root = createTableAlias(m_baseCld, null, "");
> 
> 		if (m_searchCld == m_baseCld)
> 		{
> 			m_search = m_root;
> 		}
> 		else
> 		{
> 			// PAW
> 			//m_search = getTableAlias(m_query.getObjectProjectionAttribute(), false, null, null);
> 			m_search = getTableAlias(m_query.getObjectProjectionAttribute(), false, null, null, m_query.getPathClasses());
> 		}
> 
> 		// Walk the super reference-descriptor
> 		buildSuperJoinTree(m_root, m_baseCld, "");
> 
> 		// In some cases it is necessary to split the query criteria
> 		// and then to generate UNION of several SELECTs
> 		// We build the joinTreeToCriteria mapping,
> 		if (query != null)
> 		{
> 			splitCriteria();
> 		}
> 	}
> 
> 	protected ClassDescriptor getBaseClassDescriptor()
> 	{
> 		return m_baseCld;
> 	}
> 
> 	protected ClassDescriptor getSearchClassDescriptor()
> 	{
> 		return m_searchCld;
> 	}
> 
> 	/**
> 	 * Return the TableAlias and the PathInfo for an Attribute name<br>
> 	 * field names in functions (ie: sum(name) ) are tried to resolve ie: name
> 	 * from FIELDDESCRIPTOR , UPPER(name_test) from Criteria<br>
> 	 * also resolve pathExpression adress.city or owner.konti.saldo
> 	 * @param attr
> 	 * @param useOuterJoins
> 	 * @param aUserAlias
> 	 * @return ColumnInfo
> 	 */
> 	// PAW
> 	//protected AttributeInfo getAttributeInfo(String attr, boolean useOuterJoins, String aUserAlias)
> 	protected AttributeInfo getAttributeInfo(String attr, boolean useOuterJoins, String aUserAlias, Map pathClasses)
> 	//protected AttributeInfo getAttributeInfo(SelectionCriteria crit, boolean useOuterJoins)
> 	{
> 		AttributeInfo result = new AttributeInfo();
> 		TableAlias tableAlias = null;
> 		SqlHelper.PathInfo pathInfo = SqlHelper.splitPath(attr);
> 		String colName = pathInfo.column;
> 		int sp;
> 
> 		// BRJ:
> 		// check if we refer to an attribute in the parent query
> 		// this prefix is temporary !
> 		if (colName.startsWith(Criteria.PARENT_QUERY_PREFIX) && m_parentStatement != null)
> 		{
> 			String[] fieldNameRef = {colName.substring(Criteria.PARENT_QUERY_PREFIX.length())};
> 			// PAW: is pathClasses the correct argument to pass to parent?
> 			//return m_parentStatement.getAttributeInfo(fieldNameRef[0], useOuterJoins, aUserAlias);
> 			return m_parentStatement.getAttributeInfo(fieldNameRef[0], useOuterJoins, aUserAlias, pathClasses);
> 		}
> 
> 		sp = colName.lastIndexOf(".");
> 		if (sp == -1)
> 		{
> 			tableAlias = getRoot();
> 		}
> 		else
> 		{
> 			String pathName = colName.substring(0, sp);
> 			String[] fieldNameRef = {colName.substring(sp + 1)};
> 
> 			// PAW
> 			//tableAlias = getTableAlias(pathName, useOuterJoins, aUserAlias, fieldNameRef);
> 			tableAlias = getTableAlias(pathName, useOuterJoins, aUserAlias, fieldNameRef, pathClasses);
> 			/**
> 			 * if we have not found an alias by the pathName or
> 			 * aliasName (if given), try again because pathName
> 			 * may be an aliasname. it can be only and only if it is not
> 			 * a path, which means there may be no path separators (,)
> 			 * in the pathName.
> 			 */
> 			if ((tableAlias == null) && (colName.lastIndexOf(".") == -1))
> 			{
> 				/**
> 				 * pathName might be an alias, so check this first
> 				 */
> 				// PAW
> 				//tableAlias = getTableAlias(null, useOuterJoins, pathName, null);
> 				tableAlias = getTableAlias(null, useOuterJoins, pathName, null, pathClasses);
> 			}
> 
> 			if (tableAlias != null)
> 			{
> 				// correct column name to match the alias
> 				// productGroup.groupName -> groupName
> 				pathInfo.column = fieldNameRef[0];
> 			}
> 		}
> 
> 		result.tableAlias = tableAlias;
> 		result.pathInfo = pathInfo;
> 		return result;
> 	}
> 
> 
> 	/**
> 	 * Answer the column name for alias and path info<br>
> 	 * if translate try to convert attribute name into column name otherwise use attribute name<br>
> 	 * if a FieldDescriptor is found for the attribute name the column name is taken from
> 	 * there prefixed with the alias (firstname -> A0.F_NAME).
> 	 *
> 	 * @param aTableAlias
> 	 * @param aPathInfo
> 	 * @param translate
> 	 * @return
> 	 */
> 	protected String getColName(TableAlias aTableAlias, PathInfo aPathInfo, boolean translate)
> 	{
> 		FieldDescriptor fld = null;
> 		String result = null;
> 
> 		if (translate)
> 		{
> 			// translate attribute name into column name
> 			fld = getFieldDescriptor(aTableAlias, aPathInfo);
> 
> 			if (fld != null)
> 			{
> 				// added to suport the super reference descriptor
> 				if (!fld.getClassDescriptor().getFullTableName().equals(aTableAlias.table) && aTableAlias.hasJoins())
> 				{
> 					Iterator itr = aTableAlias.joins.iterator();
> 					while (itr.hasNext())
> 					{
> 						Join join = (Join) itr.next();
> 						if (join.right.table.equals(fld.getClassDescriptor().getFullTableName()))
> 						{
> 							result = join.right.alias + "." + fld.getColumnName();
> 							break;
> 						}
> 					}
> 
> 					if (result == null)
> 					{
> 						result = aPathInfo.column;
> 					}
> 				}
> 				else
> 				{
> 					result = aTableAlias.alias + "." + fld.getColumnName();
> 				}
> 			}
> 			else if ("*".equals(aPathInfo.column))
> 			{
> 				result = aPathInfo.column;
> 			}
> 			else
> 			{
> 				// throw new IllegalArgumentException("No Field found for : " + aPathInfo.column);
> 				result = aPathInfo.column;
> 			}
> 		}
> 		else
> 		{
> 			// use attribute name
> 			result = aPathInfo.column;
> 		}
> 
> 		return result;
> 	}
> 
> 	/**
> 	 * Add the Column to the StringBuffer <br>
> 	 *
> 	 * @param aTableAlias
> 	 * @param aPathInfo
> 	 * @param translate, flag to indicate translation of pathInfo
> 	 * @param buf
> 	 * @return true if appended
> 	 */
> 	protected boolean appendColName(TableAlias aTableAlias, PathInfo aPathInfo, boolean translate, StringBuffer buf)
> 	{
> 		String prefix = aPathInfo.prefix;
> 		String suffix = aPathInfo.suffix;
> 		String colName = getColName(aTableAlias, aPathInfo, translate);
> 
> 		if (prefix != null) // rebuild function contains (
> 		{
> 			buf.append(prefix);
> 		}
> 
> 		buf.append(colName);
> 
> 		if (suffix != null) // rebuild function
> 		{
> 			buf.append(suffix);
> 		}
> 
> 		return true;
> 	}
> 
> 	/**
> 	 * Get the FieldDescriptor for the PathInfo
> 	 *
> 	 * @param aTableAlias
> 	 * @param aPathInfo
> 	 * @return FieldDescriptor
> 	 */
> 	protected FieldDescriptor getFieldDescriptor(TableAlias aTableAlias, PathInfo aPathInfo)
> 	{
> 		FieldDescriptor fld = null;
> 		String colName = aPathInfo.column;
> 
> 		if (aTableAlias != null)
> 		{
> 			fld = aTableAlias.cld.getFieldDescriptorByName(colName);
> 			if (fld == null)
> 			{
> 				ObjectReferenceDescriptor ord = aTableAlias.cld.getObjectReferenceDescriptorByName(colName);
> 				if (ord != null)
> 				{
> 					fld = getFldFromReference(aTableAlias, ord);
> 				}
> 				else
> 				{
> 					fld = getFldFromJoin(aTableAlias, colName);
> 				}
> 			}
> 		}
> 
> 		return fld;
> 	}
> 
> 	/**
> 	 * get FieldDescriptor from joined superclass
> 	 * @param aTableAlias
> 	 * @param aColName
> 	 * @return
> 	 */
> 	private FieldDescriptor getFldFromJoin(TableAlias aTableAlias, String aColName)
> 	{
> 		FieldDescriptor fld = null;
> 
> 		// Search Join Structure for attribute
> 		if (aTableAlias.joins != null)
> 		{
> 			Iterator itr = aTableAlias.joins.iterator();
> 			while (itr.hasNext())
> 			{
> 				Join join = (Join) itr.next();
> 				ClassDescriptor cld = join.right.cld;
> 
> 				if (cld != null)
> 				{
> 					fld = cld.getFieldDescriptorByName(aColName);
> 					if (fld != null)
> 					{
> 						break;
> 					}
> 
> 				}
> 			}
> 		}
> 		return fld;
> 	}
> 
> 	/**
> 	 * Get FieldDescriptor from Reference
> 	 * @param aTableAlias
> 	 * @param anOrd
> 	 * @return
> 	 */
> 	private FieldDescriptor getFldFromReference(TableAlias aTableAlias, ObjectReferenceDescriptor anOrd)
> 	{
> 		FieldDescriptor fld = null;
> 
> 		if (aTableAlias == getRoot())
> 		{
> 			// no path expression
> 			FieldDescriptor[] fk = anOrd.getForeignKeyFieldDescriptors(aTableAlias.cld);
> 			if (fk.length > 0)
> 			{
> 				fld = fk[0];
> 			}
> 		}
> 		else
> 		{
> 			// attribute with path expression
> 			/**
> 			 * MBAIRD
> 			 * potentially people are referring to objects, not to the object's primary key, 
> 			 * and then we need to take the primary key attribute of the referenced object 
> 			 * to help them out.
> 			 */
> 			ClassDescriptor cld = aTableAlias.cld.getRepository().getDescriptorFor(anOrd.getItemClass());
> 			if (cld != null)
> 			{
> 				fld = aTableAlias.cld.getFieldDescriptorByName(cld.getPkFields()[0].getPersistentField().getName());
> 			}
> 		}
> 
> 		return fld;
> 	}
> 
> 	/**
> 	 * Append the appropriate ColumnName to the buffer<br>
> 	 * if a FIELDDESCRIPTOR is found for the Criteria the colName is taken from
> 	 * there otherwise its taken from Criteria. <br>
> 	 * field names in functions (ie: sum(name) ) are tried to resolve
> 	 * ie: name from FIELDDESCRIPTOR , UPPER(name_test) from Criteria<br>
> 	 * also resolve pathExpression adress.city or owner.konti.saldo
> 	 *
> 	 * @param attr
> 	 * @param useOuterJoins
> 	 * @param aUserAlias
> 	 * @param buf
> 	 * @return
> 	 */
> 	protected boolean appendColName(String attr, boolean useOuterJoins, String aUserAlias, StringBuffer buf)
> 	{
> //		// PAW: is this correct for pathClasses ?
> 		//AttributeInfo attrInfo = getAttributeInfo(attr, useOuterJoins, aUserAlias);
> 		AttributeInfo attrInfo = getAttributeInfo(attr, useOuterJoins, aUserAlias, getQuery().getPathClasses());
> 		TableAlias tableAlias = attrInfo.tableAlias;
> 
> 		return appendColName(tableAlias, attrInfo.pathInfo, true, buf);
> 	}
> 
> 	/**
> 	 * Append the appropriate ColumnName to the buffer<br>
> 	 * if a FIELDDESCRIPTOR is found for the Criteria the colName is taken from
> 	 * there otherwise its taken from Criteria. <br>
> 	 * field names in functions (ie: sum(name) ) are tried to resolve
> 	 * ie: name from FIELDDESCRIPTOR , UPPER(name_test) from Criteria<br>
> 	 * also resolve pathExpression adress.city or owner.konti.saldo
> 	 *
> 	 * @param attr
> 	 * @param attrAlias column alias
> 	 * @param useOuterJoins
> 	 * @param aUserAlias
> 	 * @param buf
> 	 * @return
> 	 */
> 	protected boolean appendColName(String attr, String attrAlias, boolean useOuterJoins, String aUserAlias,
> 			StringBuffer buf)
> 	{
> 		// PAW
> 		//AttributeInfo attrInfo = getAttributeInfo(attr, useOuterJoins, aUserAlias);
> 		AttributeInfo attrInfo = getAttributeInfo(attr, useOuterJoins, aUserAlias, getQuery().getPathClasses());
> 		TableAlias tableAlias = attrInfo.tableAlias;
> 		PathInfo pi = attrInfo.pathInfo;
> 
> 		if (pi.suffix != null)
> 		{
> 			pi.suffix = pi.suffix + " as " + attrAlias;
> 		}
> 		else
> 		{
> 			pi.suffix = " as " + attrAlias;
> 		}
> 
> 		return appendColName(tableAlias, pi, true, buf);
> 	}
> 
> 	/**
> 	 * appends a WHERE-clause to the Statement
> 	 * @param where
> 	 * @param crit
> 	 * @param stmt
> 	 */
> 	protected void appendWhereClause(StringBuffer where, Criteria crit, StringBuffer stmt)
> 	{
> 		if (where.length() == 0)
> 		{
> 			where = null;
> 		}
> 
> 		if (where != null || (crit != null && !crit.isEmpty()))
> 		{
> 			stmt.append(" WHERE ");
> 			appendClause(where, crit, stmt);
> 		}
> 	}
> 
> 	/**
> 	 * appends a HAVING-clause to the Statement
> 	 * @param having
> 	 * @param crit
> 	 * @param stmt
> 	 */
> 	protected void appendHavingClause(StringBuffer having, Criteria crit, StringBuffer stmt)
> 	{
> 		if (having.length() == 0)
> 		{
> 			having = null;
> 		}
> 
> 		if (having != null || crit != null)
> 		{
> 			stmt.append(" HAVING ");
> 			appendClause(having, crit, stmt);
> 		}
> 	}
> 
> 	/**
> 	 * appends a WHERE/HAVING-clause to the Statement
> 	 * @param clause
> 	 * @param crit
> 	 * @param stmt
> 	 */
> 	protected void appendClause(StringBuffer clause, Criteria crit, StringBuffer stmt)
> 	{
> 		/**
> 		 * MBAIRD
> 		 * when generating the "WHERE/HAVING" clause we need to append the criteria for multi-mapped
> 		 * tables. We only need to do this for the root classdescriptor and not for joined tables
> 		 * because we assume you cannot make a relation of the wrong type upon insertion. Of course,
> 		 * you COULD mess the data up manually and this would cause a problem.
> 		 */
> 
> 		if (clause != null)
> 		{
> 			stmt.append(clause.toString());
> 		}
> 		if (crit != null)
> 		{
> 			if (clause == null)
> 			{
> 				stmt.append(asSQLStatement(crit));
> 			}
> 			else
> 			{
> 				stmt.append(" AND (");
> 				stmt.append(asSQLStatement(crit));
> 				stmt.append(")");
> 			}
> 
> 		}
> 	}
> 
> 	/**
> 	 * create SQL-String based on Criteria
> 	 * @param crit
> 	 * @return
> 	 */
> 	private String asSQLStatement(Criteria crit)
> 	{
> 		Enumeration e = crit.getElements();
> 		StringBuffer statement = new StringBuffer();
> 
> 		while (e.hasMoreElements())
> 		{
> 			Object o = e.nextElement();
> 			if (o instanceof Criteria)
> 			{
> 				String addAtStart;
> 				String addAtEnd;
> 				Criteria pc = (Criteria) o;
> 				// need to add parenthesises?
> 				if (pc.isEmbraced())
> 				{
> 					addAtStart = " (";
> 					addAtEnd = ")";
> 				}
> 				else
> 				{
> 					addAtStart = "";
> 					addAtEnd = "";
> 				}
> 
> 				switch (pc.getType())
> 				{
> 					case (Criteria.OR) :
> 						{
> 							if (statement.length() > 0)
> 							{
> 								statement.append(" OR ");
> 							}
> 							statement.append(addAtStart);
> 							statement.append(asSQLStatement(pc));
> 							statement.append(addAtEnd);
> 							break;
> 						}
> 					case (Criteria.AND) :
> 						{
> 							if (statement.length() > 0)
> 							{
> 								statement.insert(0, "( ");
> 								statement.append(") AND ");
> 							}
> 							statement.append(addAtStart);
> 							statement.append(asSQLStatement(pc));
> 							statement.append(addAtEnd);
> 							break;
> 						}
> 				}
> 			}
> 			else
> 			{
> 				SelectionCriteria c = (SelectionCriteria) o;
> 				if (statement.length() > 0)
> 				{
> 					statement.insert(0, "(");
> 					statement.append(") AND ");
> 				}
> 				appendSQLClause(c, statement);
> 			}
> 		} // while
> 
> 		// BRJ : negative Criteria surrounded by NOT (...)
> 		if (crit.isNegative())
> 		{
> 			statement.insert(0, " NOT (");
> 			statement.append(")");
> 		}
> 		return (statement.length() == 0 ? null : statement.toString());
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a BetweenCriteria
> 	 *
> 	 * @param alias
> 	 * @param pathInfo
> 	 * @param c BetweenCriteria
> 	 * @param buf
> 	 */
> 	private void appendBetweenCriteria(TableAlias alias, PathInfo pathInfo, BetweenCriteria c, StringBuffer buf)
> 	{
> 		appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
> 		buf.append(c.getClause());
> 		appendParameter(c.getValue(), buf);
> 		buf.append(" AND ");
> 		appendParameter(c.getValue2(), buf);
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for an ExistsCriteria
> 	 * @param c ExistsCriteria
> 	 */
> 	private void appendExistsCriteria(ExistsCriteria c, StringBuffer buf)
> 	{
> 		Query subQuery = (Query) c.getValue();
> 
> 		buf.append(c.getClause());
> 		appendSubQuery(subQuery, buf);
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a FieldCriteria<br>
> 	 * The value of the FieldCriteria will be translated
> 	 *
> 	 * @param alias
> 	 * @param pathInfo
> 	 * @param c ColumnCriteria
> 	 * @param buf
> 	 */
> 	private void appendFieldCriteria(TableAlias alias, PathInfo pathInfo, FieldCriteria c, StringBuffer buf)
> 	{
> 		appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
> 		buf.append(c.getClause());
> 
> 		if (c.isTranslateField())
> 		{
> 			appendColName((String) c.getValue(), false, c.getAlias(), buf);
> 		}
> 		else
> 		{
> 			buf.append(c.getValue());
> 		}
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for an InCriteria
> 	 *
> 	 * @param alias
> 	 * @param pathInfo
> 	 * @param c InCriteria
> 	 * @param buf
> 	 */
> 	private void appendInCriteria(TableAlias alias, PathInfo pathInfo, InCriteria c, StringBuffer buf)
> 	{
> 		appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
> 		buf.append(c.getClause());
> 
> 		if (c.getValue() instanceof Collection)
> 		{
> 			Object[] values = ((Collection) c.getValue()).toArray();
> 			int size = ((Collection) c.getValue()).size();
> 
> 			buf.append("(");
> 			for (int i = 0; i < size - 1; i++)
> 			{
> 				appendParameter(values[i], buf);
> 				buf.append(",");
> 			}
> 			appendParameter(values[size - 1], buf);
> 			buf.append(")");
> 		}
> 		else
> 		{
> 			appendParameter(c.getValue(), buf);
> 		}
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a NullCriteria
> 	 *
> 	 * @param alias
> 	 * @param pathInfo
> 	 * @param c NullCriteria
> 	 * @param buf
> 	 */
> 	private void appendNullCriteria(TableAlias alias, PathInfo pathInfo, NullCriteria c, StringBuffer buf)
> 	{
> 		appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
> 		buf.append(c.getClause());
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a SqlCriteria
> 	 *
> 	 */
> 	private void appendSQLCriteria(SqlCriteria c, StringBuffer buf)
> 	{
> 		buf.append(c.getClause());
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a SelectionCriteria
> 	 *
> 	 * @param c
> 	 * @param buf
> 	 */
> 	private void appendSelectionCriteria(TableAlias alias, PathInfo pathInfo, SelectionCriteria c, StringBuffer buf)
> 	{
> 		appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
> 		buf.append(c.getClause());
> 		appendParameter(c.getValue(), buf);
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a LikeCriteria
> 	 *
> 	 * @param c
> 	 * @param buf
> 	 */
> 	private void appendLikeCriteria(TableAlias alias, PathInfo pathInfo, LikeCriteria c, StringBuffer buf)
> 	{
> 		appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
> 		buf.append(c.getClause());
> 		appendParameter(c.getValue(), buf);
> 
> 		buf.append(m_platform.getEscapeClause(c));
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a SelectionCriteria
> 	 *
> 	 * @param alias
> 	 * @param pathInfo
> 	 * @param c SelectionCriteria
> 	 * @param buf
> 	 */
> 	protected void appendCriteria(TableAlias alias, PathInfo pathInfo, SelectionCriteria c, StringBuffer buf)
> 	{
> 		if (c instanceof FieldCriteria)
> 		{
> 			appendFieldCriteria(alias, pathInfo, (FieldCriteria) c, buf);
> 		}
> 		else if (c instanceof NullCriteria)
> 		{
> 			appendNullCriteria(alias, pathInfo, (NullCriteria) c, buf);
> 		}
> 		else if (c instanceof BetweenCriteria)
> 		{
> 			appendBetweenCriteria(alias, pathInfo, (BetweenCriteria) c, buf);
> 		}
> 		else if (c instanceof InCriteria)
> 		{
> 			appendInCriteria(alias, pathInfo, (InCriteria) c, buf);
> 		}
> 		else if (c instanceof SqlCriteria)
> 		{
> 			appendSQLCriteria((SqlCriteria) c, buf);
> 		}
> 		else if (c instanceof ExistsCriteria)
> 		{
> 			appendExistsCriteria((ExistsCriteria) c, buf);
> 		}
> 		else if (c instanceof LikeCriteria)
> 		{
> 			appendLikeCriteria(alias, pathInfo, (LikeCriteria) c, buf);
> 		}
> 		else
> 		{
> 			appendSelectionCriteria(alias, pathInfo, c, buf);
> 		}
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a SelectionCriteria
> 	 * If the Criteria references a class with extents an OR-Clause is
> 	 * added for each extent
> 	 * @param c SelectionCriteria
> 	 */
> 	protected void appendSQLClause(SelectionCriteria c, StringBuffer buf)
> 	{
> 		// BRJ : handle SqlCriteria
> 		if (c instanceof SqlCriteria)
> 		{
> 			buf.append(c.getAttribute());
> 		    return;
> 		}
>   	 		
> 		// BRJ : criteria attribute is a query
> 		if (c.getAttribute() instanceof Query)
> 		{
> 			Query q = (Query) c.getAttribute();
> 			buf.append("(");
> 			buf.append(getSubQuerySQL(q));
> 			buf.append(")");
> 			buf.append(c.getClause());
> 			appendParameter(c.getValue(), buf);
> 			return;
> 		}
> 
> 		// PAW
> //		AttributeInfo attrInfo = getAttributeInfo((String) c.getAttribute(), false, c.getAlias());
> 		AttributeInfo attrInfo = getAttributeInfo((String) c.getAttribute(), false, c.getAlias(), c.getPathClasses());
>   
> 		TableAlias alias = attrInfo.tableAlias;
> 
> 		if (alias != null)
> 		{
> 			boolean hasExtents = alias.hasExtents();
> 
> 			if (hasExtents)
> 			{
> 				// BRJ : surround with braces if alias has extents
> 				buf.append("(");
> 				appendCriteria(alias, attrInfo.pathInfo, c, buf);
> 
> 				c.setNumberOfExtentsToBind(alias.extents.size());
> 				Iterator iter = alias.iterateExtents();
> 				while (iter.hasNext())
> 				{
> 					TableAlias tableAlias = (TableAlias) iter.next();
> 					buf.append(" OR ");
> 					appendCriteria(tableAlias, attrInfo.pathInfo, c, buf);
> 				}
> 				buf.append(")");
> 			}
> 			else
> 			{
> 				// no extents
> 				appendCriteria(alias, attrInfo.pathInfo, c, buf);
> 			}
> 		}
> 		else
> 		{
> 			// alias null
> 			appendCriteria(alias, attrInfo.pathInfo, c, buf);
> 		}
> 
> 	}
> 
> 	/**
> 	 * Append the Parameter
> 	 * Add the place holder ? or the SubQuery
> 	 * @param value the value of the criteria
> 	 */
> 	private void appendParameter(Object value, StringBuffer buf)
> 	{
> 		if (value instanceof Query)
> 		{
> 			appendSubQuery((Query) value, buf);
> 		}
> 		else
> 		{
> 			buf.append("?");
> 		}
> 	}
> 
> 	/**
> 	 * Append a SubQuery the SQL-Clause
> 	 * @param subQuery the subQuery value of SelectionCriteria
> 	 */
> 	private void appendSubQuery(Query subQuery, StringBuffer buf)
> 	{
> 		buf.append(" (");
> 		buf.append(getSubQuerySQL(subQuery));
> 		buf.append(") ");
> 	}
> 
> 	/**
> 	 * Convert subQuery to SQL
> 	 * @param subQuery the subQuery value of SelectionCriteria
> 	 */
> 	private String getSubQuerySQL(Query subQuery)
> 	{
> 		ClassDescriptor cld = getRoot().cld.getRepository().getDescriptorFor(subQuery.getSearchClass());
> 		String sql;
> 
> 		if (subQuery instanceof QueryBySQL)
> 		{
> 			sql = ((QueryBySQL) subQuery).getSql();
> 		}
> 		else
> 		{
> 			sql = new SqlSelectStatement(this, m_platform, cld, subQuery, m_logger).getStatement();
> 		}
> 
> 		return sql;
> 	}
> 
> 	/**
> 	 * Get TableAlias by the path from the target table of the query.
> 	 * @param aPath the path from the target table of the query to this TableAlias.
> 	 * @param useOuterJoins use outer join to join this table with the previous
> 	 * table in the path.
> 	 * @param aUserAlias if specified, overrides alias in crit
> 	 * @param fieldRef String[1] contains the field name.
> 	 * In the case of related table's primary key the "related.pk" attribute
> 	 * must not add new join, but use the value of foreign key
> 	 */
> 	// PAW: copied entire getTableAlias(String, boolean, String, String[])
> 	private TableAlias getTableAlias(String aPath, boolean useOuterJoins, String aUserAlias, String[] fieldRef, Map pathClasses)
> 	{
> 		TableAlias curr, prev, indirect;
> 		String attr, attrPath = null;
> 		ObjectReferenceDescriptor ord;
> 		CollectionDescriptor cod;
> 		ClassDescriptor cld = null;
> 		Object[] prevKeys = null;
> 		Object[] keys = null;
> 		ArrayList descriptors;
> 		boolean outer = useOuterJoins;
> 		int pathLength;
> 		
> 		curr = getTableAliasForPath(aPath, aUserAlias);
> 	
> 		if (curr != null)
> 		{
> 			return curr;
> 		}
> 
> 		// PAW
> 		//descriptors = getRoot().cld.getAttributeDescriptorsForPath(aPath, getQuery().getPathClasses());
> 		descriptors = getRoot().cld.getAttributeDescriptorsForPath(aPath, pathClasses);
> 		prev = getRoot();
> 
> 		if (descriptors == null || descriptors.size() == 0)
> 		{
> 			if (prev.hasJoins())
> 			{
> 				for (Iterator itr = prev.iterateJoins(); itr.hasNext();)
> 				{
> 					prev = ((Join) itr.next()).left;
> 					// PAW
> 					//descriptors = prev.cld.getAttributeDescriptorsForPath(aPath, getQuery().getPathClasses());
> 					descriptors = prev.cld.getAttributeDescriptorsForPath(aPath, pathClasses);
> 					if (descriptors.size() > 0)
> 					{
> 						break;
> 					}
> 				}
> 			}
> 		}
> 
> 		pathLength = descriptors.size();
> 		for (int i = 0; i < pathLength; i++)
> 		{
> 			if (!(descriptors.get(i) instanceof ObjectReferenceDescriptor))
> 			{
> 				// only use Collection- and ObjectReferenceDescriptor
> 				continue;
> 			}
> 
> 			ord = (ObjectReferenceDescriptor) descriptors.get(i);
> 			attr = ord.getAttributeName();
> 			if (attrPath == null)
> 			{
> 				attrPath = attr;
> 			}
> 			else
> 			{
> 				attrPath = attrPath + "." + attr;
> 			}
> 
> 			// look for outer join hint
> 			outer = outer || getQuery().isPathOuterJoin(attr);
> 
> 			// look for 1:n or m:n
> 			if (ord instanceof CollectionDescriptor)
> 			{
> 				cod = (CollectionDescriptor) ord;
> 				// PAW
> 				//cld = getItemClassDescriptor(cod, attrPath);
> 				cld = getItemClassDescriptor(cod, attrPath, pathClasses);
> 				
> 				if (!cod.isMtoNRelation())
> 				{
> 					prevKeys = prev.cld.getPkFields();
> 					keys = cod.getForeignKeyFieldDescriptors(cld);
> 				}
> 				else
> 				{
> 					String mnAttrPath = attrPath + "*";
> 					String mnPath = aPath + "*";
> 					String mnUserAlias = (aUserAlias == null ? null : aUserAlias + "*");
> 					indirect = getTableAliasForPath(mnAttrPath, mnUserAlias, mnPath);
> 					if (indirect == null)
> 					{
> 						indirect = createTableAlias(cod.getIndirectionTable(), mnAttrPath, mnUserAlias, mnPath);
> 
> 						// we need two Joins for m:n
> 						// 1.) prev class to indirectionTable
> 						prevKeys = prev.cld.getPkFields();
> 						keys = cod.getFksToThisClass();
> 						addJoin(prev, prevKeys, indirect, keys, outer, attr + "*");
> 					}
> 					// 2.) indirectionTable to the current Class
> 					prev = indirect;
> 					prevKeys = cod.getFksToItemClass();
> 					keys = cld.getPkFields();
> 				}
> 			}
> 			else
> 			{
> 				// must be n:1 or 1:1
> 				// PAW
> 				//cld = getItemClassDescriptor(ord, attrPath);
> 				cld = getItemClassDescriptor(ord, attrPath, pathClasses);
> 
> 				prevKeys = ord.getForeignKeyFieldDescriptors(prev.cld);
> 				keys = cld.getPkFields();
> 
> 				// [olegnitz]
> 				// a special case: the last element of the path is
> 				// reference and the field is one of PK fields =>
> 				// use the correspondent foreign key field, don't add the join
> 				if ((fieldRef != null) && (i == (pathLength - 1)))
> 				{
> 					FieldDescriptor[] pk = cld.getPkFields();
> 
> 					for (int j = 0; j < pk.length; j++)
> 					{
> 						if (pk[j].getAttributeName().equals(fieldRef[0]))
> 						{
> 							fieldRef[0] = ((FieldDescriptor) prevKeys[j]).getAttributeName();
> 							return prev;
> 						}
> 					}
> 				}
> 			}
> 
> 			curr = getTableAliasForPath(attrPath, aUserAlias, aPath);
> 
> 			if (curr == null)
> 			{
> 				// PAW
> 				// List hintClasses = (List) getQuery().getPathClasses().get(aPath);
> 				List hintClasses = (List) pathClasses.get(aPath);
> 				curr = createTableAlias(cld, attrPath, aUserAlias, aPath, hintClasses);
> 
> 				outer = outer || (curr.cld == prev.cld) || curr.hasExtents() || useOuterJoins;
> 				addJoin(prev, prevKeys, curr, keys, outer, attr);
> 
> 				buildSuperJoinTree(curr, cld, aPath);
> 			}
> 
> 			prev = curr;
> 		}
> 
> 		return curr;
> 	}
> 
> 
> 	/**
> 	 * add a join between two aliases
> 	 * 
> 	 * TODO BRJ : This needs refactoring, it looks kind of weird
> 	 *
> 	 * no extents
> 	 * A1   -> A2
> 	 *
> 	 * extents on the right
> 	 * A1   -> A2
> 	 * A1   -> A2E0
> 	 *
> 	 * extents on the left : copy alias on right, extents point to copies
> 	 * A1   -> A2
> 	 * A1E0 -> A2C0
> 	 *
> 	 * extents on the left and right
> 	 * A1   -> A2
> 	 * A1   -> A2E0
> 	 * A1E0 -> A2C0
> 	 * A1E0 -> A2E0C0
> 	 *
> 	 * @param left
> 	 * @param leftKeys
> 	 * @param right
> 	 * @param rightKeys
> 	 * @param outer
> 	 * @param name
> 	 */
> 	private void addJoin(TableAlias left, Object[] leftKeys, TableAlias right, Object[] rightKeys, boolean outer,
> 			String name)
> 	{
> 		TableAlias extAlias, rightCopy;
> 
> 		left.addJoin(new Join(left, leftKeys, right, rightKeys, outer, name));
> 
> 		// build join between left and extents of right
> 		if (right.hasExtents())
> 		{
> 			for (int i = 0; i < right.extents.size(); i++)
> 			{
> 				extAlias = (TableAlias) right.extents.get(i);
> 				FieldDescriptor[] extKeys = getExtentFieldDescriptors(extAlias, (FieldDescriptor[]) rightKeys);
> 
> 				left.addJoin(new Join(left, leftKeys, extAlias, extKeys, true, name));
> 			}
> 		}
> 
> 		// we need to copy the alias on the right for each extent on the left
> 		if (left.hasExtents())
> 		{
> 			for (int i = 0; i < left.extents.size(); i++)
> 			{
> 				extAlias = (TableAlias) left.extents.get(i);
> 				FieldDescriptor[] extKeys = getExtentFieldDescriptors(extAlias, (FieldDescriptor[]) leftKeys);
> 				rightCopy = right.copy("C" + i);
> 
> 				// copies are treated like normal extents
> 				right.extents.add(rightCopy);
> 				right.extents.addAll(rightCopy.extents);
> 
> 				addJoin(extAlias, extKeys, rightCopy, rightKeys, true, name);
> 			}
> 		}
> 	}
> 
> 	/**
> 	 * Get the FieldDescriptors of the extent based on the FieldDescriptors of the parent
> 	 * @param extAlias
> 	 * @param fds
> 	 * @return
> 	 */
> 	private FieldDescriptor[] getExtentFieldDescriptors(TableAlias extAlias, FieldDescriptor[] fds)
> 	{
> 		FieldDescriptor[] result = new FieldDescriptor[fds.length];
> 
> 		for (int i = 0; i < fds.length; i++)
> 		{
> 			result[i] = extAlias.cld.getFieldDescriptorByName(fds[i].getAttributeName());
> 		}
> 
> 		return result;
> 	}
> 
> 	private char getAliasChar()
> 	{
> 		char result = 'A';
> 
> 		if (m_parentStatement != null)
> 		{
> 			result = (char) (m_parentStatement.getAliasChar() + 1);
> 		}
> 
> 		return result;
> 	}
> 
> 	/**
> 	 * Create a TableAlias for path or userAlias
> 	 * @param aCld
> 	 * @param aPath
> 	 * @param aUserAlias
> 	 * @param anOriginalPath
> 	 * @param hints a List os Class objects to be used as hints for path expressions
> 	 * @return TableAlias
> 	 *
> 	 */
> 	private TableAlias createTableAlias(ClassDescriptor aCld, String aPath, String aUserAlias, String anOriginalPath,
> 			List hints)
> 	{
> 		// PAW
> 		//if (aUserAlias == null || !anOriginalPath.equals(aPath))
> 		if (aUserAlias == null)
> 		{
> 			return createTableAlias(aCld, hints, aPath);
> 		}
> 		else
> 		{
> 			// PAW
> 			//return createTableAlias(aCld, hints, aUserAlias);
> 			return createTableAlias(aCld, hints, aUserAlias+aPath);
> 		}
> 	}
> 
> 	/**
> 	 * Create new TableAlias for path
> 	 * @param cld the class descriptor for the TableAlias
> 	 * @param path the path from the target table of the query to this TableAlias.
> 	 * @@param hints a List of Class objects to be used on path expressions
> 	 */
> 	private TableAlias createTableAlias(ClassDescriptor cld, List hints, String path)
> 	{
> 		TableAlias alias;
> 		boolean lookForExtents = false;
> 
> 		if (!cld.getExtentClasses().isEmpty() && path.length() > 0)
> 		{
> 			lookForExtents = true;
> 		}
> 
> 		String aliasName = String.valueOf(getAliasChar()) + m_aliasCount++; // m_pathToAlias.size();
> 		alias = new TableAlias(cld, aliasName, lookForExtents, hints);
> 
> 		m_pathToAlias.put(path, alias);
> 		return alias;
> 	}
> 
> 	/**
> 	 * Create a TableAlias for path or userAlias
> 	 * @param aTable
> 	 * @param aPath
> 	 * @param aUserAlias
> 	 * @param anOriginalPath
> 	 * @return TableAlias
> 	 */
> 	private TableAlias createTableAlias(String aTable, String aPath, String aUserAlias, String anOriginalPath)
> 	{
> 		// PAW
> 		//if (aUserAlias == null || !anOriginalPath.equals(aPath))
> 		if (aUserAlias == null)
> 		{
> 			return createTableAlias(aTable, aPath);
> 		}
> 		else
> 		{
> 			// PAW
> 			//return createTableAlias(aTable, aUserAlias);
> 			return createTableAlias(aTable, aUserAlias+aPath);
> 		}
> 	}
> 
> 	/**
> 	 * Create new TableAlias for path
> 	 * @param table the table name
> 	 * @param path the path from the target table of the query to this TableAlias.
> 	 */
> 	private TableAlias createTableAlias(String table, String path)
> 	{
> 		TableAlias alias;
> 
> 		if (table == null)
> 		{
> 			getLogger().warn("Creating TableAlias without table for path: " + path);
> 		}
> 
> 		String aliasName = String.valueOf(getAliasChar()) + m_aliasCount++; // + m_pathToAlias.size();
> 		alias = new TableAlias(table, aliasName);
> 		m_pathToAlias.put(path, alias);
> 
> 		return alias;
> 	}
> 
> 	/**
> 	 * Answer the TableAlias for aPath
> 	 * @param aPath
> 	 * @return TableAlias, null if none
> 	 */
> 	private TableAlias getTableAliasForPath(String aPath)
> 	{
> 		return (TableAlias) m_pathToAlias.get(aPath);
> 	}
> 
> 	/**
> 	 * Answer the TableAlias for aPath or aUserAlias
> 	 * @param aPath
> 	 * @param aUserAlias
> 	 * @return TableAlias, null if none
> 	 */
> 	private TableAlias getTableAliasForPath(String aPath, String aUserAlias)
> 	{
> 		if (aUserAlias == null)
> 		{
> 			return getTableAliasForPath(aPath);
> 		}
> 		else
> 		{
> 			// PAW
> 			// return getTableAliasForPath(aUserAlias);
> 			return getTableAliasForPath(aUserAlias+aPath);
> 		}
> 	}
> 
> 	/**
> 	 * Answer the TableAlias for aPath or aUserAlias
> 	 * @param aPath
> 	 * @param aUserAlias
> 	 * @param anOriginalPath
> 	 * @return TableAlias, null if none
> 	 */
> 	private TableAlias getTableAliasForPath(String aPath, String aUserAlias, String anOriginalPath)
> 	{
> 		// PAW
> 		//if (aUserAlias == null || !anOriginalPath.equals(aPath))
> 		if (aUserAlias == null)
> 		{
> 			return getTableAliasForPath(aPath);
> 		}
> 		else
> 		{
> 			// PAW
> 			//return getTableAliasForPath(aUserAlias);
> 			return getTableAliasForPath(aUserAlias+aPath);
> 		}
> 	}
> 
> 	/**
> 	 * TODO: add super ClassDescriptor
> 	 * answer the ClassDescriptor for itemClass for an ObjectReferenceDescriptor
> 	 * check optional hint;
> 	 */
> 	// PAW
> //	private ClassDescriptor getItemClassDescriptor(ObjectReferenceDescriptor ord, String attr)
> 	private ClassDescriptor getItemClassDescriptor(ObjectReferenceDescriptor ord, String attr, Map pathClasses)
> 	{
> 		// PAW
> 		//List itemClasses = null;
> //		  Query q = getQuery();
> //
> //		  if (q instanceof QueryByCriteria)
> //		  {
> //			  itemClasses = ((QueryByCriteria) q).getClassesForPath(attr);
> //		  }
> 		List itemClasses = (List)pathClasses.get(attr);
> 
> 		if (itemClasses == null)
> 		{
> 			itemClasses = new ArrayList();
> 			itemClasses.add(ord.getItemClass());
> 		}
> 
> 		List classDescriptors = new ArrayList(itemClasses.size());
> 		DescriptorRepository repo = ord.getClassDescriptor().getRepository();
> 
> 		for (Iterator iter = itemClasses.iterator(); iter.hasNext();)
> 		{
> 			Class clazz = (Class) iter.next();
> 			classDescriptors.add(repo.getDescriptorFor(clazz));
> 		}
> 		return (ClassDescriptor) classDescriptors.get(0);
> 
> 	}
> 
> 
> 	/**
> 	 * Appends to the statement the ORDER BY clause for the Query
> 	 */
> 	protected void appendOrderByClause(List orderByFields, int[] orderByColumnNumbers, StringBuffer buf)
> 	{
> 		FieldHelper cf;
> 
> 		if (orderByColumnNumbers == null)
> 		{
> 			return;
> 		}
> 
> 		buf.append(" ORDER BY ");
> 		for (int i = 0; i < orderByColumnNumbers.length; i++)
> 		{
> 			cf = (FieldHelper) orderByFields.get(i);
> 			if (i > 0)
> 			{
> 				buf.append(",");
> 			}
> 			buf.append(orderByColumnNumbers[i]);
> 			if (!cf.isAscending)
> 			{
> 				buf.append(" DESC");
> 			}
> 		}
> 	}
> 
> 	/**
> 	 * Appends to the statement the GROUP BY clause for the Query
> 	 */
> 	protected void appendGroupByClause(List groupByFields, StringBuffer buf)
> 	{
> 		FieldHelper cf;
> 
> 		if (groupByFields == null || groupByFields.size() == 0)
> 		{
> 			return;
> 		}
> 
> 		buf.append(" GROUP BY ");
> 		for (int i = 0; i < groupByFields.size(); i++)
> 		{
> 			cf = (FieldHelper) groupByFields.get(i);
> 			if (i > 0)
> 			{
> 				buf.append(",");
> 			}
> 			appendColName(cf.name, false, null, buf);
> 		}
> 	}
> 
> 	/**
> 	 * Appends to the statement table and all tables joined to it.
> 	 * @param alias the table alias
> 	 * @param where append conditions for WHERE clause here
> 	 */
> 	protected void appendTableWithJoins(TableAlias alias, StringBuffer where, StringBuffer buf)
> 	{
> 		int stmtFromPos = 0;
> 		byte joinSyntax = getJoinSyntaxType();
> 
> 		if (joinSyntax == SQL92_JOIN_SYNTAX)
> 		{
> 			stmtFromPos = buf.length(); // store position of join (by: Terry Dexter)
> 		}
> 
> 		if (!(joinSyntax == SQL92_NOPAREN_JOIN_SYNTAX && alias != getRoot()))
> 		{
> 			buf.append(alias.getTableAndAlias());
> 
> 			if (getQuery() instanceof MtoNQuery)
> 			{
> 				buf.append(",");
> 				buf.append(((MtoNQuery) getQuery()).getIndirectionTable());
> 			}
> 		}
> 
> 		if (!alias.hasJoins())
> 		{
> 			return;
> 		}
> 
> 		for (Iterator it = alias.iterateJoins(); it.hasNext();)
> 		{
> 			Join join = (Join) it.next();
> 
> 			if (joinSyntax == SQL92_JOIN_SYNTAX)
> 			{
> 				appendJoinSQL92(join, where, buf);
> 				if (it.hasNext())
> 				{
> 					buf.insert(stmtFromPos, "(");
> 					buf.append(")");
> 				}
> 			}
> 			else if (joinSyntax == SQL92_NOPAREN_JOIN_SYNTAX)
> 			{
> 				appendJoinSQL92NoParen(join, where, buf);
> 			}
> 			else
> 			{
> 				appendJoin(where, buf, join);
> 			}
> 
> 		}
> 	}
> 
> 	/**
> 	 * Append Join for non SQL92 Syntax
> 	 */
> 	private void appendJoin(StringBuffer where, StringBuffer buf, Join join)
> 	{
> 		buf.append(",");
> 		appendTableWithJoins(join.right, where, buf);
> 		if (where.length() > 0)
> 		{
> 			where.append(" AND ");
> 		}
> 		join.appendJoinEqualities(where);
> 	}
> 
> 	/**
> 	 * Append Join for SQL92 Syntax
> 	 */
> 	private void appendJoinSQL92(Join join, StringBuffer where, StringBuffer buf)
> 	{
> 		if (join.isOuter)
> 		{
> 			buf.append(" LEFT OUTER JOIN ");
> 		}
> 		else
> 		{
> 			buf.append(" INNER JOIN ");
> 		}
> 		if (join.right.hasJoins())
> 		{
> 			buf.append("(");
> 			appendTableWithJoins(join.right, where, buf);
> 			buf.append(")");
> 		}
> 		else
> 		{
> 			appendTableWithJoins(join.right, where, buf);
> 		}
> 		buf.append(" ON ");
> 		join.appendJoinEqualities(buf);
> 	}
> 
> 	/**
> 	 * Append Join for SQL92 Syntax without parentheses
> 	 */
> 	private void appendJoinSQL92NoParen(Join join, StringBuffer where, StringBuffer buf)
> 	{
> 		if (join.isOuter)
> 		{
> 			buf.append(" LEFT OUTER JOIN ");
> 		}
> 		else
> 		{
> 			buf.append(" INNER JOIN ");
> 		}
> 
> 		buf.append(join.right.getTableAndAlias());
> 		buf.append(" ON ");
> 		join.appendJoinEqualities(buf);
> 
> 		appendTableWithJoins(join.right, where, buf);
> 	}
> 
> 	/**
> 	 * Appends to the statement columns if they are not found among the existingColumns.
> 	 * Columns added here use a column-alias "ojb_col_x", x being the number of existing columns
> 	 * @param columns the list of columns represented by Criteria.Field to ensure
> 	 * @param existingColumns the list of column names (String) that are already appended
> 	 * @return the array of column numbers (base 1)
> 	 */
> 	protected int[] ensureColumns(List columns, List existingColumns, StringBuffer buf)
> 	{
> 		FieldHelper cf;
> 		int[] columnNumbers = new int[columns.size()];
> 
> 		for (int i = 0; i < columnNumbers.length; i++)
> 		{
> 			cf = (FieldHelper) columns.get(i);
> 			columnNumbers[i] = existingColumns.indexOf(cf.name);
> 			if (columnNumbers[i] == -1)
> 			{
> 				columnNumbers[i] = existingColumns.size();
> 				existingColumns.add(cf.name);
> 				buf.append(",");
> 				appendColName(cf.name, "ojb_col_" + columnNumbers[i], false, null, buf);
> 			}
> 			columnNumbers[i]++; // columns numbers have base 1
> 		}
> 		return columnNumbers;
> 	}
> 
> 	/**
> 	 * Build the tree of joins for the given criteria
> 	 */
> 	private void buildJoinTree(Criteria crit)
> 	{
> 		Enumeration e = crit.getElements();
> 
> 		while (e.hasMoreElements())
> 		{
> 			Object o = e.nextElement();
> 			if (o instanceof Criteria)
> 			{
> 				buildJoinTree((Criteria) o);
> 			}
> 			else
> 			{
> 				SelectionCriteria c = (SelectionCriteria) o;
> 				
> 				// BRJ skip SqlCriteria
> 				if (c instanceof SqlCriteria)
> 				{
> 					continue;
> 				}				
> 				
> 				// BRJ: Outer join for OR
> 				boolean useOuterJoin = (crit.getType() == Criteria.OR);
> 
> 				// BRJ: do not build join tree for subQuery attribute                  
> 				if (c.getAttribute() != null && c.getAttribute() instanceof String)
> 				{
> 					// PAW
> 					//buildJoinTreeForColumn((String) c.getAttribute(), useOuterJoin, c.getAlias());
> 					buildJoinTreeForColumn((String) c.getAttribute(), useOuterJoin, c.getAlias(), c.getPathClasses());
> 				}
> 				if (c instanceof FieldCriteria)
> 				{
> 					FieldCriteria cc = (FieldCriteria) c;
> 					// PAW
> 					//buildJoinTreeForColumn((String) cc.getValue(), useOuterJoin, c.getAlias());
> 					buildJoinTreeForColumn((String) cc.getValue(), useOuterJoin, c.getAlias(), c.getPathClasses());
> 				}
> 			}
> 		}
> 	}
> 
> 	/**
> 	 * build the Join-Information for name
> 	 * functions and the last segment are removed
> 	 * ie: avg(accounts.amount) -> accounts
> 	 */
> 	// PAW
> 	//private void buildJoinTreeForColumn(String aColName, boolean useOuterJoin, String aUserAlias)
> 	private void buildJoinTreeForColumn(String aColName, boolean useOuterJoin, String aUserAlias, Map pathClasses)
> 	{
> 		String pathName = SqlHelper.cleanPath(aColName);
> 		int sepPos = pathName.lastIndexOf(".");
> 		
> 		if (sepPos >= 0)
> 		{
> 			// PAW
> 			//getTableAlias(pathName.substring(0, sepPos), useOuterJoin, aUserAlias, new String[]{pathName
> 			//		.substring(sepPos + 1)});
> 			getTableAlias(pathName.substring(0, sepPos), useOuterJoin, aUserAlias,
> 						  new String[]{pathName.substring(sepPos + 1)}, pathClasses);
> 		}
> 	}
> 
> 	/**
> 	 * build the Join-Information if a super reference exists
> 	 *
> 	 * @param left
> 	 * @param cld
> 	 * @param name
> 	 */
> 	protected void buildSuperJoinTree(TableAlias left, ClassDescriptor cld, String name)
> 	{
> 		Iterator objRefs = cld.getObjectReferenceDescriptors().iterator();
> 		while (objRefs.hasNext())
> 		{
> 			ObjectReferenceDescriptor objRef = (ObjectReferenceDescriptor) objRefs.next();
> 			FieldDescriptor[] leftFields = objRef.getForeignKeyFieldDescriptors(cld);
> 
> 			ClassDescriptor refCld = cld.getRepository().getDescriptorFor(objRef.getItemClassName());
> 			if (objRef.getPersistentField() instanceof AnonymousPersistentFieldForInheritance)
> 			{
> 				TableAlias base_alias = getTableAliasForPath(name, null);
> 
> 				String aliasName = String.valueOf(getAliasChar()) + m_aliasCount++;
> 				TableAlias right = new TableAlias(refCld, aliasName, false, null);
> 
> 				Join join1to1 = new Join(left, leftFields, right, refCld.getPkFields(), false, "superClass");
> 				base_alias.addJoin(join1to1);
> 
> 				buildSuperJoinTree(right, refCld, name);
> 			}
> 		}
> 	}
> 
> 	/**
> 	 * First reduce the Criteria to the normal disjunctive form, then
> 	 * calculate the necessary tree of joined tables for each item, then group
> 	 * items with the same tree of joined tables.
> 	 */
> 	protected void splitCriteria()
> 	{
> 		Criteria whereCrit = getQuery().getCriteria();
> 		Criteria havingCrit = getQuery().getHavingCriteria();
> 
> 		if (whereCrit == null || whereCrit.isEmpty())
> 		{
> 			getJoinTreeToCriteria().put(getRoot(), null);
> 		}
> 		else
> 		{
> 			// TODO: parameters list shold be modified when the form is reduced to DNF.
> 			getJoinTreeToCriteria().put(getRoot(), whereCrit);
> 			buildJoinTree(whereCrit);
> 		}
> 
> 		if (havingCrit == null || havingCrit.isEmpty())
> 		{
> 		}
> 		else
> 		{
> 			buildJoinTree(havingCrit);
> 		}
> 
> 	}
> 
> 	/**
> 	 * Gets the query.
> 	 * @return Returns a Query
> 	 */
> 	protected QueryByCriteria getQuery()
> 	{
> 		return m_query;
> 	}
> 
> 	/**
> 	 * Gets the root.
> 	 * @return Returns a TableAlias
> 	 */
> 	protected TableAlias getRoot()
> 	{
> 		return m_root;
> 	}
> 
> 	/**
> 	 * Sets the root.
> 	 * @param root The root to set
> 	 */
> 	protected void setRoot(TableAlias root)
> 	{
> 		this.m_root = root;
> 	}
> 
> 	/**
> 	 * Gets the search table of this query.
> 	 * @return Returns a TableAlias
> 	 */
> 	protected TableAlias getSearchTable()
> 	{
> 		return m_search;
> 	}
> 
> 	/**
> 	 * Gets the joinTreeToCriteria.
> 	 * @return Returns a HashMap
> 	 */
> 	protected HashMap getJoinTreeToCriteria()
> 	{
> 		return m_joinTreeToCriteria;
> 	}
> 
> 	/**
> 	 * Returns the joinSyntaxType.
> 	 * @return byte
> 	 */
> 	protected byte getJoinSyntaxType()
> 	{
> 		return m_platform.getJoinSyntaxType();
> 	}
> 
> 	/**
> 	 * Returns the logger.
> 	 * @return Logger
> 	 */
> 	protected Logger getLogger()
> 	{
> 		return m_logger;
> 	}
> 
> 	//-----------------------------------------------------------------
> 	// ------------------- Inner classes ------------------------------
> 	//-----------------------------------------------------------------
> 
> 	/**
> 	 * This class is a helper to return TableAlias and PathInfo
> 	 */
> 	static final class AttributeInfo
> 	{
> 		TableAlias tableAlias;
> 		PathInfo pathInfo;
> 	}
> 
> 	/**
> 	 * This class represents one table (possibly with alias) in the SQL query
> 	 */
> 	static final class TableAlias
> 	{
> 		Logger logger = LoggerFactory.getLogger(TableAlias.class);
> 		ClassDescriptor cld; // Is null for indirection table of M:N relation
> 		String table;
> 		final String alias;
> 		List extents = new ArrayList();
> 		List hints = new ArrayList();
> 		List joins;
> 
> 		TableAlias(String aTable, String anAlias)
> 		{
> 			this.cld = null;
> 			this.table = aTable;
> 			this.alias = anAlias;
> 		}
> 
> 		TableAlias(ClassDescriptor aCld, String anAlias)
> 		{
> 			this(aCld, anAlias, false, null);
> 		}
> 
> 		TableAlias(ClassDescriptor aCld, String anAlias, boolean lookForExtents, List hints)
> 		{
> 			this.cld = aCld;
> 			this.table = aCld.getFullTableName();
> 			this.alias = anAlias;
> 			boolean useHintsOnExtents = false;
> 
> 			//LEANDRO: use hints
> 			if (hints != null && hints.size() > 0)
> 			{
> 				useHintsOnExtents = true;
> 			}
> 
> 			logger.debug("TableAlias(): using hints ? " + useHintsOnExtents);
> 
> 			// BRJ : build alias for extents, only one per Table
> 			if (lookForExtents)
> 			{
> 				ClassDescriptor[] extCLDs = (ClassDescriptor[]) aCld.getRepository().getAllConcreteSubclassDescriptors(
> 						aCld).toArray(new ClassDescriptor[0]);
> 
> 				ClassDescriptor extCd;
> 				Class extClass;
> 				String extTable;
> 				Map extMap = new HashMap(); // only one Alias per Table
> 				int firtsNonAbstractExtentIndex = 0;
> 
> 				for (int i = 0; i < extCLDs.length; i++)
> 				{
> 					extCd = extCLDs[i];
> 					extClass = extCd.getClassOfObject();
> 					if (useHintsOnExtents && (!hints.contains(extClass)))
> 					{
> 						//LEANDRO: don't include this class
> 						logger.debug("Skipping class [" + extClass + "] from extents List");
> 						firtsNonAbstractExtentIndex++;
> 						continue;
> 					}
> 					extTable = extCd.getFullTableName();
> 
> 					// BRJ : Use the first non abstract extent
> 					// if the main cld is abstract
> 					//logger.debug("cld abstract["+aCld.isAbstract()+"] i["+i+"] index ["+firtsNonAbstractExtentIndex+"]");
> 					if (aCld.isAbstract() && i == firtsNonAbstractExtentIndex)
> 					{
> 						this.cld = extCd;
> 						this.table = extTable;
> 					}
> 					else
> 					{
> 						// Add a new extent entry only if the table of the extent
> 						// does not match the table of the 'base' class.
> 						if (extMap.get(extTable) == null && !extTable.equals(table))
> 						{
> 							extMap.put(extTable, new TableAlias(extCd, anAlias + "E" + i, false, hints));
> 						}
> 					}
> 				}
> 				extents.addAll(extMap.values());
> 			}
> 
> 			if (cld == null)
> 			{
> 				throw new PersistenceBrokerSQLException("Table is NULL for alias: " + alias);
> 			}
> 		}
> 
> 		ClassDescriptor getClassDescriptor()
> 		{
> 			return cld;
> 		}
> 
> 		String getTableAndAlias()
> 		{
> 			return table + " " + alias;
> 		}
> 
> 		boolean hasExtents()
> 		{
> 			return (!extents.isEmpty());
> 		}
> 
> 		Iterator iterateExtents()
> 		{
> 			return extents.iterator();
> 		}
> 
> 		/**
> 		 * Copy the Alias and all it's extents adding a Postfix
> 		 * Joins are not copied
> 		 * @param aPostfix
> 		 * @return
> 		 */
> 		TableAlias copy(String aPostfix)
> 		{
> 			TableAlias result, temp;
> 			Iterator iter = iterateExtents();
> 
> 			if (cld == null)
> 			{
> 				result = new TableAlias(table, alias + aPostfix);
> 			}
> 			else
> 			{
> 				result = new TableAlias(cld, alias + aPostfix);
> 			}
> 
> 			while (iter.hasNext())
> 			{
> 				temp = (TableAlias) iter.next();
> 				result.extents.add(temp.copy(aPostfix));
> 			}
> 
> 			return result;
> 		}
> 
> 		void addJoin(Join join)
> 		{
> 			if (joins == null)
> 			{
> 				joins = new ArrayList();
> 			}
> 			joins.add(join);
> 		}
> 
> 		Iterator iterateJoins()
> 		{
> 			return joins.iterator();
> 		}
> 
> 		boolean hasJoins()
> 		{
> 			return (joins != null);
> 		}
> 
> 		public String toString()
> 		{
> 			StringBuffer sb = new StringBuffer(1024);
> 			boolean first = true;
> 
> 			sb.append(getTableAndAlias());
> 			if (joins != null)
> 			{
> 				sb.append(" [");
> 				for (Iterator it = joins.iterator(); it.hasNext();)
> 				{
> 					Join join = (Join) it.next();
> 
> 					if (first)
> 					{
> 						first = false;
> 					}
> 					else
> 					{
> 						sb.append(", ");
> 					}
> 					sb.append("-(");
> 					sb.append(join.name);
> 					sb.append(")->");
> 					sb.append(join.right);
> 				}
> 				sb.append("]");
> 			}
> 			return sb.toString();
> 		}
> 
> 		public boolean equals(Object obj)
> 		{
> 			TableAlias t = (TableAlias) obj;
> 
> 			return table.equals(t.table); // BRJ: check table only
> 		}
> 
> 		public int hashCode()
> 		{
> 			return table.hashCode();
> 		}
> 
> 	}
> 
> 	/**
> 	 * This class represents join between two TableAliases
> 	 */
> 	final class Join
> 	{
> 		final TableAlias left;
> 		final String[] leftKeys;
> 		final TableAlias right;
> 		final String[] rightKeys;
> 		final boolean isOuter;
> 		/** This is the name of the field corresponding to this join */
> 		final String name;
> 
> 		/**
> 		 * leftKeys and rightKeys should be either FieldDescriptor[] or String[]
> 		 */
> 		Join(TableAlias left, Object[] leftKeys, TableAlias right, Object[] rightKeys, boolean isOuter, String name)
> 		{
> 			this.left = left;
> 			this.leftKeys = getColumns(leftKeys);
> 			this.right = right;
> 			this.rightKeys = getColumns(rightKeys);
> 			this.isOuter = isOuter;
> 			this.name = name;
> 		}
> 
> 		private String[] getColumns(Object[] keys)
> 		{
> 			String[] columns = new String[keys.length];
> 
> 			if (keys instanceof FieldDescriptor[])
> 			{
> 				FieldDescriptor[] kd = (FieldDescriptor[]) keys;
> 				for (int i = 0; i < columns.length; i++)
> 				{
> 					columns[i] = kd[i].getColumnName();
> 				}
> 			}
> 			else
> 			{
> 				for (int i = 0; i < columns.length; i++)
> 				{
> 					columns[i] = keys[i].toString();
> 				}
> 			}
> 			return columns;
> 		}
> 
> 		void appendJoinEqualities(StringBuffer buf)
> 		{
> 			byte joinSyntax = getJoinSyntaxType();
> 
> 			for (int i = 0; i < leftKeys.length; i++)
> 			{
> 				if (i > 0)
> 				{
> 					buf.append(" AND ");
> 				}
> 				buf.append(left.alias);
> 				buf.append(".");
> 				buf.append(leftKeys[i]);
> 
> 				if (isOuter && joinSyntax == SYBASE_JOIN_SYNTAX)
> 				{
> 					buf.append("*=");
> 				}
> 				else
> 				{
> 					buf.append("=");
> 				}
> 
> 				buf.append(right.alias);
> 				buf.append(".");
> 				buf.append(rightKeys[i]);
> 
> 				if (isOuter && joinSyntax == ORACLE_JOIN_SYNTAX)
> 				{
> 					buf.append("(+)");
> 				}
> 			}
> 		}
> 
> 		public boolean equals(Object obj)
> 		{
> 			Join j = (Join) obj;
> 			return name.equals(j.name) && (isOuter == j.isOuter) && right.equals(j.right);
> 		}
> 
> 		public int hashCode()
> 		{
> 			return name.hashCode();
> 		}
> 
> 		public String toString()
> 		{
> 			return left.alias + " -> " + right.alias;
> 		}
> 	}
> }
> 
> 
> 
> 
> ------------------------------------------------------------------------
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: ojb-user-unsubscribe@db.apache.org
> For additional commands, e-mail: ojb-user-help@db.apache.org

---------------------------------------------------------------------
To unsubscribe, e-mail: ojb-user-unsubscribe@db.apache.org
For additional commands, e-mail: ojb-user-help@db.apache.org


Re: Per-criteria pathClass [was ojb-user: Query casts] 2 of 2

Posted by Phil Warrick <ph...@mcgill.ca>.
Hi Jakob,

I just wanted to be sure that you now have all the files, namely

src files
o.a.o.b.query.Criteria.java
o.a.o.b.query.SelectionCriteria.java
o.a.o.b.query.BetweenCriteria.java
o.a.o.b.accesslayer.sql.SqlQueryStatement.java
o.a.o.b.accesslayer.sql.SqlSelectStatement.java

srcTest files
ojbTest-schema-pathClass.xml
repository-junit-pathClass.xml
o.a.o.b.query.A.java
o.a.o.b.query.B.java
o.a.o.b.query.C.java
o.a.o.b.query.C1.java
o.a.o.b.query.D.java
o.a.o.b.query.PathClassTest.java

Once you've had a moment to look over what I've done, I'll look at the 
alias-by-segment issue.

Phil

> Hi Jakob,
> 
> I applied my changes to 1.70 and am sending you the entire java file.  I
> created a patch file with eclipse, but it did not seem to want to do an
> intelligent diff (just -entire old file contents and +entire new file
> contents).  There was no improvement if I turned off ignore whitespace
> in the compare preferences.  Maybe there's some eclipse trick I need to
> know here.
> 
> Anyway, hopefully now you have all my work related to this change.
> 
> Looking forward to your feedback,
> 
> Phil
> 
> 
> ------------------------------------------------------------------------
> 
> package org.apache.ojb.broker.accesslayer.sql;
> 
> /* Copyright  2004 The Apache Software Foundation
>  *
>  * Licensed under the Apache License, Version 2.0 (the "License");
>  * you may not use this file except in compliance with the License.
>  * You may obtain a copy of the License at
>  *
>  *     http://www.apache.org/licenses/LICENSE-2.0
>  *
>  * Unless required by applicable law or agreed to in writing, software
>  * distributed under the License is distributed on an "AS IS" BASIS,
>  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
>  * See the License for the specific language governing permissions and
>  * limitations under the License.
>  */
> 
> import java.util.ArrayList;
> import java.util.Collection;
> import java.util.Enumeration;
> import java.util.HashMap;
> import java.util.Iterator;
> import java.util.List;
> import java.util.Map;
> 
> import org.apache.ojb.broker.PersistenceBrokerSQLException;
> import org.apache.ojb.broker.accesslayer.JoinSyntaxTypes;
> import org.apache.ojb.broker.metadata.ClassDescriptor;
> import org.apache.ojb.broker.metadata.CollectionDescriptor;
> import org.apache.ojb.broker.metadata.DescriptorRepository;
> import org.apache.ojb.broker.metadata.FieldDescriptor;
> import org.apache.ojb.broker.metadata.FieldHelper;
> import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
> import org.apache.ojb.broker.metadata.fieldaccess.AnonymousPersistentFieldForInheritance;
> import org.apache.ojb.broker.platforms.Platform;
> import org.apache.ojb.broker.query.*;
> import org.apache.ojb.broker.util.SqlHelper;
> import org.apache.ojb.broker.util.SqlHelper.PathInfo;
> import org.apache.ojb.broker.util.logging.Logger;
> import org.apache.ojb.broker.util.logging.LoggerFactory;
> 
> /**
>  * Model a Statement based on Query.
>  *
>  * @author <a href="mailto:jbraeuchi@gmx.ch">Jakob Braeuchi</a>
>  * @version $Id: SqlQueryStatement.java,v 1.68 2004/03/11 18:16:08 brianm Exp $
>  */
> public abstract class SqlQueryStatement implements SqlStatement, JoinSyntaxTypes
> {
> 	private SqlQueryStatement m_parentStatement;
> 	/** the logger */
> 	private Logger m_logger;
> 	/** the target table of the query */
> 	private TableAlias m_root;
> 	/** the search table of the query */
> 	private TableAlias m_search;
> 	/** the query */
> 	private QueryByCriteria m_query;
> 	/** the mapping of paths to TableAliases */
> 	private HashMap m_pathToAlias = new HashMap();
> 	/** maps trees of joins to criteria */
> 	private HashMap m_joinTreeToCriteria = new HashMap();
> 
> 	private Platform m_platform;
> 	private ClassDescriptor m_baseCld;
> 	private ClassDescriptor m_searchCld;
> 
> 	private int m_aliasCount = 0;
> 
> 	/**
> 	 * Constructor for SqlCriteriaStatement.
> 	 *
> 	 * @param pf the Platform
> 	 * @param cld the ClassDescriptor
> 	 * @param query the Query
> 	 * @param logger the Logger
> 	 */
> 	public SqlQueryStatement(Platform pf, ClassDescriptor cld, Query query, Logger logger)
> 	{
> 		this(null, pf, cld, query, logger);
> 	}
> 
> 	/**
> 	 * Constructor for SqlCriteriaStatement.
> 	 *
> 	 * @param parent the Parent Query
> 	 * @param pf the Platform
> 	 * @param cld the ClassDescriptor
> 	 * @param query the Query
> 	 * @param logger the Logger
> 	 */
> 	public SqlQueryStatement(SqlQueryStatement parent, Platform pf, ClassDescriptor cld, Query query, Logger logger)
> 	{
> 		m_logger = logger != null ? logger : LoggerFactory.getLogger(SqlQueryStatement.class);
> 		m_parentStatement = parent;
> 		m_query = (QueryByCriteria) query;
> 		m_platform = pf;
> 		m_searchCld = cld;
> 
> 		if ((m_query == null) || (m_query.getBaseClass() == m_query.getSearchClass()))
> 		{
> 			m_baseCld = m_searchCld;
> 		}
> 		else
> 		{
> 			m_baseCld = cld.getRepository().getDescriptorFor(query.getBaseClass());
> 		}
> 
> 		m_root = createTableAlias(m_baseCld, null, "");
> 
> 		if (m_searchCld == m_baseCld)
> 		{
> 			m_search = m_root;
> 		}
> 		else
> 		{
> 			// PAW
> 			//m_search = getTableAlias(m_query.getObjectProjectionAttribute(), false, null, null);
> 			m_search = getTableAlias(m_query.getObjectProjectionAttribute(), false, null, null, m_query.getPathClasses());
> 		}
> 
> 		// Walk the super reference-descriptor
> 		buildSuperJoinTree(m_root, m_baseCld, "");
> 
> 		// In some cases it is necessary to split the query criteria
> 		// and then to generate UNION of several SELECTs
> 		// We build the joinTreeToCriteria mapping,
> 		if (query != null)
> 		{
> 			splitCriteria();
> 		}
> 	}
> 
> 	protected ClassDescriptor getBaseClassDescriptor()
> 	{
> 		return m_baseCld;
> 	}
> 
> 	protected ClassDescriptor getSearchClassDescriptor()
> 	{
> 		return m_searchCld;
> 	}
> 
> 	/**
> 	 * Return the TableAlias and the PathInfo for an Attribute name<br>
> 	 * field names in functions (ie: sum(name) ) are tried to resolve ie: name
> 	 * from FIELDDESCRIPTOR , UPPER(name_test) from Criteria<br>
> 	 * also resolve pathExpression adress.city or owner.konti.saldo
> 	 * @param attr
> 	 * @param useOuterJoins
> 	 * @param aUserAlias
> 	 * @return ColumnInfo
> 	 */
> 	// PAW
> 	//protected AttributeInfo getAttributeInfo(String attr, boolean useOuterJoins, String aUserAlias)
> 	protected AttributeInfo getAttributeInfo(String attr, boolean useOuterJoins, String aUserAlias, Map pathClasses)
> 	//protected AttributeInfo getAttributeInfo(SelectionCriteria crit, boolean useOuterJoins)
> 	{
> 		AttributeInfo result = new AttributeInfo();
> 		TableAlias tableAlias = null;
> 		SqlHelper.PathInfo pathInfo = SqlHelper.splitPath(attr);
> 		String colName = pathInfo.column;
> 		int sp;
> 
> 		// BRJ:
> 		// check if we refer to an attribute in the parent query
> 		// this prefix is temporary !
> 		if (colName.startsWith(Criteria.PARENT_QUERY_PREFIX) && m_parentStatement != null)
> 		{
> 			String[] fieldNameRef = {colName.substring(Criteria.PARENT_QUERY_PREFIX.length())};
> 			// PAW: is pathClasses the correct argument to pass to parent?
> 			//return m_parentStatement.getAttributeInfo(fieldNameRef[0], useOuterJoins, aUserAlias);
> 			return m_parentStatement.getAttributeInfo(fieldNameRef[0], useOuterJoins, aUserAlias, pathClasses);
> 		}
> 
> 		sp = colName.lastIndexOf(".");
> 		if (sp == -1)
> 		{
> 			tableAlias = getRoot();
> 		}
> 		else
> 		{
> 			String pathName = colName.substring(0, sp);
> 			String[] fieldNameRef = {colName.substring(sp + 1)};
> 
> 			// PAW
> 			//tableAlias = getTableAlias(pathName, useOuterJoins, aUserAlias, fieldNameRef);
> 			tableAlias = getTableAlias(pathName, useOuterJoins, aUserAlias, fieldNameRef, pathClasses);
> 			/**
> 			 * if we have not found an alias by the pathName or
> 			 * aliasName (if given), try again because pathName
> 			 * may be an aliasname. it can be only and only if it is not
> 			 * a path, which means there may be no path separators (,)
> 			 * in the pathName.
> 			 */
> 			if ((tableAlias == null) && (colName.lastIndexOf(".") == -1))
> 			{
> 				/**
> 				 * pathName might be an alias, so check this first
> 				 */
> 				// PAW
> 				//tableAlias = getTableAlias(null, useOuterJoins, pathName, null);
> 				tableAlias = getTableAlias(null, useOuterJoins, pathName, null, pathClasses);
> 			}
> 
> 			if (tableAlias != null)
> 			{
> 				// correct column name to match the alias
> 				// productGroup.groupName -> groupName
> 				pathInfo.column = fieldNameRef[0];
> 			}
> 		}
> 
> 		result.tableAlias = tableAlias;
> 		result.pathInfo = pathInfo;
> 		return result;
> 	}
> 
> 
> 	/**
> 	 * Answer the column name for alias and path info<br>
> 	 * if translate try to convert attribute name into column name otherwise use attribute name<br>
> 	 * if a FieldDescriptor is found for the attribute name the column name is taken from
> 	 * there prefixed with the alias (firstname -> A0.F_NAME).
> 	 *
> 	 * @param aTableAlias
> 	 * @param aPathInfo
> 	 * @param translate
> 	 * @return
> 	 */
> 	protected String getColName(TableAlias aTableAlias, PathInfo aPathInfo, boolean translate)
> 	{
> 		FieldDescriptor fld = null;
> 		String result = null;
> 
> 		if (translate)
> 		{
> 			// translate attribute name into column name
> 			fld = getFieldDescriptor(aTableAlias, aPathInfo);
> 
> 			if (fld != null)
> 			{
> 				// added to suport the super reference descriptor
> 				if (!fld.getClassDescriptor().getFullTableName().equals(aTableAlias.table) && aTableAlias.hasJoins())
> 				{
> 					Iterator itr = aTableAlias.joins.iterator();
> 					while (itr.hasNext())
> 					{
> 						Join join = (Join) itr.next();
> 						if (join.right.table.equals(fld.getClassDescriptor().getFullTableName()))
> 						{
> 							result = join.right.alias + "." + fld.getColumnName();
> 							break;
> 						}
> 					}
> 
> 					if (result == null)
> 					{
> 						result = aPathInfo.column;
> 					}
> 				}
> 				else
> 				{
> 					result = aTableAlias.alias + "." + fld.getColumnName();
> 				}
> 			}
> 			else if ("*".equals(aPathInfo.column))
> 			{
> 				result = aPathInfo.column;
> 			}
> 			else
> 			{
> 				// throw new IllegalArgumentException("No Field found for : " + aPathInfo.column);
> 				result = aPathInfo.column;
> 			}
> 		}
> 		else
> 		{
> 			// use attribute name
> 			result = aPathInfo.column;
> 		}
> 
> 		return result;
> 	}
> 
> 	/**
> 	 * Add the Column to the StringBuffer <br>
> 	 *
> 	 * @param aTableAlias
> 	 * @param aPathInfo
> 	 * @param translate, flag to indicate translation of pathInfo
> 	 * @param buf
> 	 * @return true if appended
> 	 */
> 	protected boolean appendColName(TableAlias aTableAlias, PathInfo aPathInfo, boolean translate, StringBuffer buf)
> 	{
> 		String prefix = aPathInfo.prefix;
> 		String suffix = aPathInfo.suffix;
> 		String colName = getColName(aTableAlias, aPathInfo, translate);
> 
> 		if (prefix != null) // rebuild function contains (
> 		{
> 			buf.append(prefix);
> 		}
> 
> 		buf.append(colName);
> 
> 		if (suffix != null) // rebuild function
> 		{
> 			buf.append(suffix);
> 		}
> 
> 		return true;
> 	}
> 
> 	/**
> 	 * Get the FieldDescriptor for the PathInfo
> 	 *
> 	 * @param aTableAlias
> 	 * @param aPathInfo
> 	 * @return FieldDescriptor
> 	 */
> 	protected FieldDescriptor getFieldDescriptor(TableAlias aTableAlias, PathInfo aPathInfo)
> 	{
> 		FieldDescriptor fld = null;
> 		String colName = aPathInfo.column;
> 
> 		if (aTableAlias != null)
> 		{
> 			fld = aTableAlias.cld.getFieldDescriptorByName(colName);
> 			if (fld == null)
> 			{
> 				ObjectReferenceDescriptor ord = aTableAlias.cld.getObjectReferenceDescriptorByName(colName);
> 				if (ord != null)
> 				{
> 					fld = getFldFromReference(aTableAlias, ord);
> 				}
> 				else
> 				{
> 					fld = getFldFromJoin(aTableAlias, colName);
> 				}
> 			}
> 		}
> 
> 		return fld;
> 	}
> 
> 	/**
> 	 * get FieldDescriptor from joined superclass
> 	 * @param aTableAlias
> 	 * @param aColName
> 	 * @return
> 	 */
> 	private FieldDescriptor getFldFromJoin(TableAlias aTableAlias, String aColName)
> 	{
> 		FieldDescriptor fld = null;
> 
> 		// Search Join Structure for attribute
> 		if (aTableAlias.joins != null)
> 		{
> 			Iterator itr = aTableAlias.joins.iterator();
> 			while (itr.hasNext())
> 			{
> 				Join join = (Join) itr.next();
> 				ClassDescriptor cld = join.right.cld;
> 
> 				if (cld != null)
> 				{
> 					fld = cld.getFieldDescriptorByName(aColName);
> 					if (fld != null)
> 					{
> 						break;
> 					}
> 
> 				}
> 			}
> 		}
> 		return fld;
> 	}
> 
> 	/**
> 	 * Get FieldDescriptor from Reference
> 	 * @param aTableAlias
> 	 * @param anOrd
> 	 * @return
> 	 */
> 	private FieldDescriptor getFldFromReference(TableAlias aTableAlias, ObjectReferenceDescriptor anOrd)
> 	{
> 		FieldDescriptor fld = null;
> 
> 		if (aTableAlias == getRoot())
> 		{
> 			// no path expression
> 			FieldDescriptor[] fk = anOrd.getForeignKeyFieldDescriptors(aTableAlias.cld);
> 			if (fk.length > 0)
> 			{
> 				fld = fk[0];
> 			}
> 		}
> 		else
> 		{
> 			// attribute with path expression
> 			/**
> 			 * MBAIRD
> 			 * potentially people are referring to objects, not to the object's primary key, 
> 			 * and then we need to take the primary key attribute of the referenced object 
> 			 * to help them out.
> 			 */
> 			ClassDescriptor cld = aTableAlias.cld.getRepository().getDescriptorFor(anOrd.getItemClass());
> 			if (cld != null)
> 			{
> 				fld = aTableAlias.cld.getFieldDescriptorByName(cld.getPkFields()[0].getPersistentField().getName());
> 			}
> 		}
> 
> 		return fld;
> 	}
> 
> 	/**
> 	 * Append the appropriate ColumnName to the buffer<br>
> 	 * if a FIELDDESCRIPTOR is found for the Criteria the colName is taken from
> 	 * there otherwise its taken from Criteria. <br>
> 	 * field names in functions (ie: sum(name) ) are tried to resolve
> 	 * ie: name from FIELDDESCRIPTOR , UPPER(name_test) from Criteria<br>
> 	 * also resolve pathExpression adress.city or owner.konti.saldo
> 	 *
> 	 * @param attr
> 	 * @param useOuterJoins
> 	 * @param aUserAlias
> 	 * @param buf
> 	 * @return
> 	 */
> 	protected boolean appendColName(String attr, boolean useOuterJoins, String aUserAlias, StringBuffer buf)
> 	{
> //		// PAW: is this correct for pathClasses ?
> 		//AttributeInfo attrInfo = getAttributeInfo(attr, useOuterJoins, aUserAlias);
> 		AttributeInfo attrInfo = getAttributeInfo(attr, useOuterJoins, aUserAlias, getQuery().getPathClasses());
> 		TableAlias tableAlias = attrInfo.tableAlias;
> 
> 		return appendColName(tableAlias, attrInfo.pathInfo, true, buf);
> 	}
> 
> 	/**
> 	 * Append the appropriate ColumnName to the buffer<br>
> 	 * if a FIELDDESCRIPTOR is found for the Criteria the colName is taken from
> 	 * there otherwise its taken from Criteria. <br>
> 	 * field names in functions (ie: sum(name) ) are tried to resolve
> 	 * ie: name from FIELDDESCRIPTOR , UPPER(name_test) from Criteria<br>
> 	 * also resolve pathExpression adress.city or owner.konti.saldo
> 	 *
> 	 * @param attr
> 	 * @param attrAlias column alias
> 	 * @param useOuterJoins
> 	 * @param aUserAlias
> 	 * @param buf
> 	 * @return
> 	 */
> 	protected boolean appendColName(String attr, String attrAlias, boolean useOuterJoins, String aUserAlias,
> 			StringBuffer buf)
> 	{
> 		// PAW
> 		//AttributeInfo attrInfo = getAttributeInfo(attr, useOuterJoins, aUserAlias);
> 		AttributeInfo attrInfo = getAttributeInfo(attr, useOuterJoins, aUserAlias, getQuery().getPathClasses());
> 		TableAlias tableAlias = attrInfo.tableAlias;
> 		PathInfo pi = attrInfo.pathInfo;
> 
> 		if (pi.suffix != null)
> 		{
> 			pi.suffix = pi.suffix + " as " + attrAlias;
> 		}
> 		else
> 		{
> 			pi.suffix = " as " + attrAlias;
> 		}
> 
> 		return appendColName(tableAlias, pi, true, buf);
> 	}
> 
> 	/**
> 	 * appends a WHERE-clause to the Statement
> 	 * @param where
> 	 * @param crit
> 	 * @param stmt
> 	 */
> 	protected void appendWhereClause(StringBuffer where, Criteria crit, StringBuffer stmt)
> 	{
> 		if (where.length() == 0)
> 		{
> 			where = null;
> 		}
> 
> 		if (where != null || (crit != null && !crit.isEmpty()))
> 		{
> 			stmt.append(" WHERE ");
> 			appendClause(where, crit, stmt);
> 		}
> 	}
> 
> 	/**
> 	 * appends a HAVING-clause to the Statement
> 	 * @param having
> 	 * @param crit
> 	 * @param stmt
> 	 */
> 	protected void appendHavingClause(StringBuffer having, Criteria crit, StringBuffer stmt)
> 	{
> 		if (having.length() == 0)
> 		{
> 			having = null;
> 		}
> 
> 		if (having != null || crit != null)
> 		{
> 			stmt.append(" HAVING ");
> 			appendClause(having, crit, stmt);
> 		}
> 	}
> 
> 	/**
> 	 * appends a WHERE/HAVING-clause to the Statement
> 	 * @param clause
> 	 * @param crit
> 	 * @param stmt
> 	 */
> 	protected void appendClause(StringBuffer clause, Criteria crit, StringBuffer stmt)
> 	{
> 		/**
> 		 * MBAIRD
> 		 * when generating the "WHERE/HAVING" clause we need to append the criteria for multi-mapped
> 		 * tables. We only need to do this for the root classdescriptor and not for joined tables
> 		 * because we assume you cannot make a relation of the wrong type upon insertion. Of course,
> 		 * you COULD mess the data up manually and this would cause a problem.
> 		 */
> 
> 		if (clause != null)
> 		{
> 			stmt.append(clause.toString());
> 		}
> 		if (crit != null)
> 		{
> 			if (clause == null)
> 			{
> 				stmt.append(asSQLStatement(crit));
> 			}
> 			else
> 			{
> 				stmt.append(" AND (");
> 				stmt.append(asSQLStatement(crit));
> 				stmt.append(")");
> 			}
> 
> 		}
> 	}
> 
> 	/**
> 	 * create SQL-String based on Criteria
> 	 * @param crit
> 	 * @return
> 	 */
> 	private String asSQLStatement(Criteria crit)
> 	{
> 		Enumeration e = crit.getElements();
> 		StringBuffer statement = new StringBuffer();
> 
> 		while (e.hasMoreElements())
> 		{
> 			Object o = e.nextElement();
> 			if (o instanceof Criteria)
> 			{
> 				String addAtStart;
> 				String addAtEnd;
> 				Criteria pc = (Criteria) o;
> 				// need to add parenthesises?
> 				if (pc.isEmbraced())
> 				{
> 					addAtStart = " (";
> 					addAtEnd = ")";
> 				}
> 				else
> 				{
> 					addAtStart = "";
> 					addAtEnd = "";
> 				}
> 
> 				switch (pc.getType())
> 				{
> 					case (Criteria.OR) :
> 						{
> 							if (statement.length() > 0)
> 							{
> 								statement.append(" OR ");
> 							}
> 							statement.append(addAtStart);
> 							statement.append(asSQLStatement(pc));
> 							statement.append(addAtEnd);
> 							break;
> 						}
> 					case (Criteria.AND) :
> 						{
> 							if (statement.length() > 0)
> 							{
> 								statement.insert(0, "( ");
> 								statement.append(") AND ");
> 							}
> 							statement.append(addAtStart);
> 							statement.append(asSQLStatement(pc));
> 							statement.append(addAtEnd);
> 							break;
> 						}
> 				}
> 			}
> 			else
> 			{
> 				SelectionCriteria c = (SelectionCriteria) o;
> 				if (statement.length() > 0)
> 				{
> 					statement.insert(0, "(");
> 					statement.append(") AND ");
> 				}
> 				appendSQLClause(c, statement);
> 			}
> 		} // while
> 
> 		// BRJ : negative Criteria surrounded by NOT (...)
> 		if (crit.isNegative())
> 		{
> 			statement.insert(0, " NOT (");
> 			statement.append(")");
> 		}
> 		return (statement.length() == 0 ? null : statement.toString());
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a BetweenCriteria
> 	 *
> 	 * @param alias
> 	 * @param pathInfo
> 	 * @param c BetweenCriteria
> 	 * @param buf
> 	 */
> 	private void appendBetweenCriteria(TableAlias alias, PathInfo pathInfo, BetweenCriteria c, StringBuffer buf)
> 	{
> 		appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
> 		buf.append(c.getClause());
> 		appendParameter(c.getValue(), buf);
> 		buf.append(" AND ");
> 		appendParameter(c.getValue2(), buf);
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for an ExistsCriteria
> 	 * @param c ExistsCriteria
> 	 */
> 	private void appendExistsCriteria(ExistsCriteria c, StringBuffer buf)
> 	{
> 		Query subQuery = (Query) c.getValue();
> 
> 		buf.append(c.getClause());
> 		appendSubQuery(subQuery, buf);
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a FieldCriteria<br>
> 	 * The value of the FieldCriteria will be translated
> 	 *
> 	 * @param alias
> 	 * @param pathInfo
> 	 * @param c ColumnCriteria
> 	 * @param buf
> 	 */
> 	private void appendFieldCriteria(TableAlias alias, PathInfo pathInfo, FieldCriteria c, StringBuffer buf)
> 	{
> 		appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
> 		buf.append(c.getClause());
> 
> 		if (c.isTranslateField())
> 		{
> 			appendColName((String) c.getValue(), false, c.getAlias(), buf);
> 		}
> 		else
> 		{
> 			buf.append(c.getValue());
> 		}
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for an InCriteria
> 	 *
> 	 * @param alias
> 	 * @param pathInfo
> 	 * @param c InCriteria
> 	 * @param buf
> 	 */
> 	private void appendInCriteria(TableAlias alias, PathInfo pathInfo, InCriteria c, StringBuffer buf)
> 	{
> 		appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
> 		buf.append(c.getClause());
> 
> 		if (c.getValue() instanceof Collection)
> 		{
> 			Object[] values = ((Collection) c.getValue()).toArray();
> 			int size = ((Collection) c.getValue()).size();
> 
> 			buf.append("(");
> 			for (int i = 0; i < size - 1; i++)
> 			{
> 				appendParameter(values[i], buf);
> 				buf.append(",");
> 			}
> 			appendParameter(values[size - 1], buf);
> 			buf.append(")");
> 		}
> 		else
> 		{
> 			appendParameter(c.getValue(), buf);
> 		}
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a NullCriteria
> 	 *
> 	 * @param alias
> 	 * @param pathInfo
> 	 * @param c NullCriteria
> 	 * @param buf
> 	 */
> 	private void appendNullCriteria(TableAlias alias, PathInfo pathInfo, NullCriteria c, StringBuffer buf)
> 	{
> 		appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
> 		buf.append(c.getClause());
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a SqlCriteria
> 	 *
> 	 */
> 	private void appendSQLCriteria(SqlCriteria c, StringBuffer buf)
> 	{
> 		buf.append(c.getClause());
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a SelectionCriteria
> 	 *
> 	 * @param c
> 	 * @param buf
> 	 */
> 	private void appendSelectionCriteria(TableAlias alias, PathInfo pathInfo, SelectionCriteria c, StringBuffer buf)
> 	{
> 		appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
> 		buf.append(c.getClause());
> 		appendParameter(c.getValue(), buf);
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a LikeCriteria
> 	 *
> 	 * @param c
> 	 * @param buf
> 	 */
> 	private void appendLikeCriteria(TableAlias alias, PathInfo pathInfo, LikeCriteria c, StringBuffer buf)
> 	{
> 		appendColName(alias, pathInfo, c.isTranslateAttribute(), buf);
> 		buf.append(c.getClause());
> 		appendParameter(c.getValue(), buf);
> 
> 		buf.append(m_platform.getEscapeClause(c));
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a SelectionCriteria
> 	 *
> 	 * @param alias
> 	 * @param pathInfo
> 	 * @param c SelectionCriteria
> 	 * @param buf
> 	 */
> 	protected void appendCriteria(TableAlias alias, PathInfo pathInfo, SelectionCriteria c, StringBuffer buf)
> 	{
> 		if (c instanceof FieldCriteria)
> 		{
> 			appendFieldCriteria(alias, pathInfo, (FieldCriteria) c, buf);
> 		}
> 		else if (c instanceof NullCriteria)
> 		{
> 			appendNullCriteria(alias, pathInfo, (NullCriteria) c, buf);
> 		}
> 		else if (c instanceof BetweenCriteria)
> 		{
> 			appendBetweenCriteria(alias, pathInfo, (BetweenCriteria) c, buf);
> 		}
> 		else if (c instanceof InCriteria)
> 		{
> 			appendInCriteria(alias, pathInfo, (InCriteria) c, buf);
> 		}
> 		else if (c instanceof SqlCriteria)
> 		{
> 			appendSQLCriteria((SqlCriteria) c, buf);
> 		}
> 		else if (c instanceof ExistsCriteria)
> 		{
> 			appendExistsCriteria((ExistsCriteria) c, buf);
> 		}
> 		else if (c instanceof LikeCriteria)
> 		{
> 			appendLikeCriteria(alias, pathInfo, (LikeCriteria) c, buf);
> 		}
> 		else
> 		{
> 			appendSelectionCriteria(alias, pathInfo, c, buf);
> 		}
> 	}
> 
> 	/**
> 	 * Answer the SQL-Clause for a SelectionCriteria
> 	 * If the Criteria references a class with extents an OR-Clause is
> 	 * added for each extent
> 	 * @param c SelectionCriteria
> 	 */
> 	protected void appendSQLClause(SelectionCriteria c, StringBuffer buf)
> 	{
> 		// BRJ : handle SqlCriteria
> 		if (c instanceof SqlCriteria)
> 		{
> 			buf.append(c.getAttribute());
> 		    return;
> 		}
>   	 		
> 		// BRJ : criteria attribute is a query
> 		if (c.getAttribute() instanceof Query)
> 		{
> 			Query q = (Query) c.getAttribute();
> 			buf.append("(");
> 			buf.append(getSubQuerySQL(q));
> 			buf.append(")");
> 			buf.append(c.getClause());
> 			appendParameter(c.getValue(), buf);
> 			return;
> 		}
> 
> 		// PAW
> //		AttributeInfo attrInfo = getAttributeInfo((String) c.getAttribute(), false, c.getAlias());
> 		AttributeInfo attrInfo = getAttributeInfo((String) c.getAttribute(), false, c.getAlias(), c.getPathClasses());
>   
> 		TableAlias alias = attrInfo.tableAlias;
> 
> 		if (alias != null)
> 		{
> 			boolean hasExtents = alias.hasExtents();
> 
> 			if (hasExtents)
> 			{
> 				// BRJ : surround with braces if alias has extents
> 				buf.append("(");
> 				appendCriteria(alias, attrInfo.pathInfo, c, buf);
> 
> 				c.setNumberOfExtentsToBind(alias.extents.size());
> 				Iterator iter = alias.iterateExtents();
> 				while (iter.hasNext())
> 				{
> 					TableAlias tableAlias = (TableAlias) iter.next();
> 					buf.append(" OR ");
> 					appendCriteria(tableAlias, attrInfo.pathInfo, c, buf);
> 				}
> 				buf.append(")");
> 			}
> 			else
> 			{
> 				// no extents
> 				appendCriteria(alias, attrInfo.pathInfo, c, buf);
> 			}
> 		}
> 		else
> 		{
> 			// alias null
> 			appendCriteria(alias, attrInfo.pathInfo, c, buf);
> 		}
> 
> 	}
> 
> 	/**
> 	 * Append the Parameter
> 	 * Add the place holder ? or the SubQuery
> 	 * @param value the value of the criteria
> 	 */
> 	private void appendParameter(Object value, StringBuffer buf)
> 	{
> 		if (value instanceof Query)
> 		{
> 			appendSubQuery((Query) value, buf);
> 		}
> 		else
> 		{
> 			buf.append("?");
> 		}
> 	}
> 
> 	/**
> 	 * Append a SubQuery the SQL-Clause
> 	 * @param subQuery the subQuery value of SelectionCriteria
> 	 */
> 	private void appendSubQuery(Query subQuery, StringBuffer buf)
> 	{
> 		buf.append(" (");
> 		buf.append(getSubQuerySQL(subQuery));
> 		buf.append(") ");
> 	}
> 
> 	/**
> 	 * Convert subQuery to SQL
> 	 * @param subQuery the subQuery value of SelectionCriteria
> 	 */
> 	private String getSubQuerySQL(Query subQuery)
> 	{
> 		ClassDescriptor cld = getRoot().cld.getRepository().getDescriptorFor(subQuery.getSearchClass());
> 		String sql;
> 
> 		if (subQuery instanceof QueryBySQL)
> 		{
> 			sql = ((QueryBySQL) subQuery).getSql();
> 		}
> 		else
> 		{
> 			sql = new SqlSelectStatement(this, m_platform, cld, subQuery, m_logger).getStatement();
> 		}
> 
> 		return sql;
> 	}
> 
> 	/**
> 	 * Get TableAlias by the path from the target table of the query.
> 	 * @param aPath the path from the target table of the query to this TableAlias.
> 	 * @param useOuterJoins use outer join to join this table with the previous
> 	 * table in the path.
> 	 * @param aUserAlias if specified, overrides alias in crit
> 	 * @param fieldRef String[1] contains the field name.
> 	 * In the case of related table's primary key the "related.pk" attribute
> 	 * must not add new join, but use the value of foreign key
> 	 */
> 	// PAW: copied entire getTableAlias(String, boolean, String, String[])
> 	private TableAlias getTableAlias(String aPath, boolean useOuterJoins, String aUserAlias, String[] fieldRef, Map pathClasses)
> 	{
> 		TableAlias curr, prev, indirect;
> 		String attr, attrPath = null;
> 		ObjectReferenceDescriptor ord;
> 		CollectionDescriptor cod;
> 		ClassDescriptor cld = null;
> 		Object[] prevKeys = null;
> 		Object[] keys = null;
> 		ArrayList descriptors;
> 		boolean outer = useOuterJoins;
> 		int pathLength;
> 		
> 		curr = getTableAliasForPath(aPath, aUserAlias);
> 	
> 		if (curr != null)
> 		{
> 			return curr;
> 		}
> 
> 		// PAW
> 		//descriptors = getRoot().cld.getAttributeDescriptorsForPath(aPath, getQuery().getPathClasses());
> 		descriptors = getRoot().cld.getAttributeDescriptorsForPath(aPath, pathClasses);
> 		prev = getRoot();
> 
> 		if (descriptors == null || descriptors.size() == 0)
> 		{
> 			if (prev.hasJoins())
> 			{
> 				for (Iterator itr = prev.iterateJoins(); itr.hasNext();)
> 				{
> 					prev = ((Join) itr.next()).left;
> 					// PAW
> 					//descriptors = prev.cld.getAttributeDescriptorsForPath(aPath, getQuery().getPathClasses());
> 					descriptors = prev.cld.getAttributeDescriptorsForPath(aPath, pathClasses);
> 					if (descriptors.size() > 0)
> 					{
> 						break;
> 					}
> 				}
> 			}
> 		}
> 
> 		pathLength = descriptors.size();
> 		for (int i = 0; i < pathLength; i++)
> 		{
> 			if (!(descriptors.get(i) instanceof ObjectReferenceDescriptor))
> 			{
> 				// only use Collection- and ObjectReferenceDescriptor
> 				continue;
> 			}
> 
> 			ord = (ObjectReferenceDescriptor) descriptors.get(i);
> 			attr = ord.getAttributeName();
> 			if (attrPath == null)
> 			{
> 				attrPath = attr;
> 			}
> 			else
> 			{
> 				attrPath = attrPath + "." + attr;
> 			}
> 
> 			// look for outer join hint
> 			outer = outer || getQuery().isPathOuterJoin(attr);
> 
> 			// look for 1:n or m:n
> 			if (ord instanceof CollectionDescriptor)
> 			{
> 				cod = (CollectionDescriptor) ord;
> 				// PAW
> 				//cld = getItemClassDescriptor(cod, attrPath);
> 				cld = getItemClassDescriptor(cod, attrPath, pathClasses);
> 				
> 				if (!cod.isMtoNRelation())
> 				{
> 					prevKeys = prev.cld.getPkFields();
> 					keys = cod.getForeignKeyFieldDescriptors(cld);
> 				}
> 				else
> 				{
> 					String mnAttrPath = attrPath + "*";
> 					String mnPath = aPath + "*";
> 					String mnUserAlias = (aUserAlias == null ? null : aUserAlias + "*");
> 					indirect = getTableAliasForPath(mnAttrPath, mnUserAlias, mnPath);
> 					if (indirect == null)
> 					{
> 						indirect = createTableAlias(cod.getIndirectionTable(), mnAttrPath, mnUserAlias, mnPath);
> 
> 						// we need two Joins for m:n
> 						// 1.) prev class to indirectionTable
> 						prevKeys = prev.cld.getPkFields();
> 						keys = cod.getFksToThisClass();
> 						addJoin(prev, prevKeys, indirect, keys, outer, attr + "*");
> 					}
> 					// 2.) indirectionTable to the current Class
> 					prev = indirect;
> 					prevKeys = cod.getFksToItemClass();
> 					keys = cld.getPkFields();
> 				}
> 			}
> 			else
> 			{
> 				// must be n:1 or 1:1
> 				// PAW
> 				//cld = getItemClassDescriptor(ord, attrPath);
> 				cld = getItemClassDescriptor(ord, attrPath, pathClasses);
> 
> 				prevKeys = ord.getForeignKeyFieldDescriptors(prev.cld);
> 				keys = cld.getPkFields();
> 
> 				// [olegnitz]
> 				// a special case: the last element of the path is
> 				// reference and the field is one of PK fields =>
> 				// use the correspondent foreign key field, don't add the join
> 				if ((fieldRef != null) && (i == (pathLength - 1)))
> 				{
> 					FieldDescriptor[] pk = cld.getPkFields();
> 
> 					for (int j = 0; j < pk.length; j++)
> 					{
> 						if (pk[j].getAttributeName().equals(fieldRef[0]))
> 						{
> 							fieldRef[0] = ((FieldDescriptor) prevKeys[j]).getAttributeName();
> 							return prev;
> 						}
> 					}
> 				}
> 			}
> 
> 			curr = getTableAliasForPath(attrPath, aUserAlias, aPath);
> 
> 			if (curr == null)
> 			{
> 				// PAW
> 				// List hintClasses = (List) getQuery().getPathClasses().get(aPath);
> 				List hintClasses = (List) pathClasses.get(aPath);
> 				curr = createTableAlias(cld, attrPath, aUserAlias, aPath, hintClasses);
> 
> 				outer = outer || (curr.cld == prev.cld) || curr.hasExtents() || useOuterJoins;
> 				addJoin(prev, prevKeys, curr, keys, outer, attr);
> 
> 				buildSuperJoinTree(curr, cld, aPath);
> 			}
> 
> 			prev = curr;
> 		}
> 
> 		return curr;
> 	}
> 
> 
> 	/**
> 	 * add a join between two aliases
> 	 * 
> 	 * TODO BRJ : This needs refactoring, it looks kind of weird
> 	 *
> 	 * no extents
> 	 * A1   -> A2
> 	 *
> 	 * extents on the right
> 	 * A1   -> A2
> 	 * A1   -> A2E0
> 	 *
> 	 * extents on the left : copy alias on right, extents point to copies
> 	 * A1   -> A2
> 	 * A1E0 -> A2C0
> 	 *
> 	 * extents on the left and right
> 	 * A1   -> A2
> 	 * A1   -> A2E0
> 	 * A1E0 -> A2C0
> 	 * A1E0 -> A2E0C0
> 	 *
> 	 * @param left
> 	 * @param leftKeys
> 	 * @param right
> 	 * @param rightKeys
> 	 * @param outer
> 	 * @param name
> 	 */
> 	private void addJoin(TableAlias left, Object[] leftKeys, TableAlias right, Object[] rightKeys, boolean outer,
> 			String name)
> 	{
> 		TableAlias extAlias, rightCopy;
> 
> 		left.addJoin(new Join(left, leftKeys, right, rightKeys, outer, name));
> 
> 		// build join between left and extents of right
> 		if (right.hasExtents())
> 		{
> 			for (int i = 0; i < right.extents.size(); i++)
> 			{
> 				extAlias = (TableAlias) right.extents.get(i);
> 				FieldDescriptor[] extKeys = getExtentFieldDescriptors(extAlias, (FieldDescriptor[]) rightKeys);
> 
> 				left.addJoin(new Join(left, leftKeys, extAlias, extKeys, true, name));
> 			}
> 		}
> 
> 		// we need to copy the alias on the right for each extent on the left
> 		if (left.hasExtents())
> 		{
> 			for (int i = 0; i < left.extents.size(); i++)
> 			{
> 				extAlias = (TableAlias) left.extents.get(i);
> 				FieldDescriptor[] extKeys = getExtentFieldDescriptors(extAlias, (FieldDescriptor[]) leftKeys);
> 				rightCopy = right.copy("C" + i);
> 
> 				// copies are treated like normal extents
> 				right.extents.add(rightCopy);
> 				right.extents.addAll(rightCopy.extents);
> 
> 				addJoin(extAlias, extKeys, rightCopy, rightKeys, true, name);
> 			}
> 		}
> 	}
> 
> 	/**
> 	 * Get the FieldDescriptors of the extent based on the FieldDescriptors of the parent
> 	 * @param extAlias
> 	 * @param fds
> 	 * @return
> 	 */
> 	private FieldDescriptor[] getExtentFieldDescriptors(TableAlias extAlias, FieldDescriptor[] fds)
> 	{
> 		FieldDescriptor[] result = new FieldDescriptor[fds.length];
> 
> 		for (int i = 0; i < fds.length; i++)
> 		{
> 			result[i] = extAlias.cld.getFieldDescriptorByName(fds[i].getAttributeName());
> 		}
> 
> 		return result;
> 	}
> 
> 	private char getAliasChar()
> 	{
> 		char result = 'A';
> 
> 		if (m_parentStatement != null)
> 		{
> 			result = (char) (m_parentStatement.getAliasChar() + 1);
> 		}
> 
> 		return result;
> 	}
> 
> 	/**
> 	 * Create a TableAlias for path or userAlias
> 	 * @param aCld
> 	 * @param aPath
> 	 * @param aUserAlias
> 	 * @param anOriginalPath
> 	 * @param hints a List os Class objects to be used as hints for path expressions
> 	 * @return TableAlias
> 	 *
> 	 */
> 	private TableAlias createTableAlias(ClassDescriptor aCld, String aPath, String aUserAlias, String anOriginalPath,
> 			List hints)
> 	{
> 		// PAW
> 		//if (aUserAlias == null || !anOriginalPath.equals(aPath))
> 		if (aUserAlias == null)
> 		{
> 			return createTableAlias(aCld, hints, aPath);
> 		}
> 		else
> 		{
> 			// PAW
> 			//return createTableAlias(aCld, hints, aUserAlias);
> 			return createTableAlias(aCld, hints, aUserAlias+aPath);
> 		}
> 	}
> 
> 	/**
> 	 * Create new TableAlias for path
> 	 * @param cld the class descriptor for the TableAlias
> 	 * @param path the path from the target table of the query to this TableAlias.
> 	 * @@param hints a List of Class objects to be used on path expressions
> 	 */
> 	private TableAlias createTableAlias(ClassDescriptor cld, List hints, String path)
> 	{
> 		TableAlias alias;
> 		boolean lookForExtents = false;
> 
> 		if (!cld.getExtentClasses().isEmpty() && path.length() > 0)
> 		{
> 			lookForExtents = true;
> 		}
> 
> 		String aliasName = String.valueOf(getAliasChar()) + m_aliasCount++; // m_pathToAlias.size();
> 		alias = new TableAlias(cld, aliasName, lookForExtents, hints);
> 
> 		m_pathToAlias.put(path, alias);
> 		return alias;
> 	}
> 
> 	/**
> 	 * Create a TableAlias for path or userAlias
> 	 * @param aTable
> 	 * @param aPath
> 	 * @param aUserAlias
> 	 * @param anOriginalPath
> 	 * @return TableAlias
> 	 */
> 	private TableAlias createTableAlias(String aTable, String aPath, String aUserAlias, String anOriginalPath)
> 	{
> 		// PAW
> 		//if (aUserAlias == null || !anOriginalPath.equals(aPath))
> 		if (aUserAlias == null)
> 		{
> 			return createTableAlias(aTable, aPath);
> 		}
> 		else
> 		{
> 			// PAW
> 			//return createTableAlias(aTable, aUserAlias);
> 			return createTableAlias(aTable, aUserAlias+aPath);
> 		}
> 	}
> 
> 	/**
> 	 * Create new TableAlias for path
> 	 * @param table the table name
> 	 * @param path the path from the target table of the query to this TableAlias.
> 	 */
> 	private TableAlias createTableAlias(String table, String path)
> 	{
> 		TableAlias alias;
> 
> 		if (table == null)
> 		{
> 			getLogger().warn("Creating TableAlias without table for path: " + path);
> 		}
> 
> 		String aliasName = String.valueOf(getAliasChar()) + m_aliasCount++; // + m_pathToAlias.size();
> 		alias = new TableAlias(table, aliasName);
> 		m_pathToAlias.put(path, alias);
> 
> 		return alias;
> 	}
> 
> 	/**
> 	 * Answer the TableAlias for aPath
> 	 * @param aPath
> 	 * @return TableAlias, null if none
> 	 */
> 	private TableAlias getTableAliasForPath(String aPath)
> 	{
> 		return (TableAlias) m_pathToAlias.get(aPath);
> 	}
> 
> 	/**
> 	 * Answer the TableAlias for aPath or aUserAlias
> 	 * @param aPath
> 	 * @param aUserAlias
> 	 * @return TableAlias, null if none
> 	 */
> 	private TableAlias getTableAliasForPath(String aPath, String aUserAlias)
> 	{
> 		if (aUserAlias == null)
> 		{
> 			return getTableAliasForPath(aPath);
> 		}
> 		else
> 		{
> 			// PAW
> 			// return getTableAliasForPath(aUserAlias);
> 			return getTableAliasForPath(aUserAlias+aPath);
> 		}
> 	}
> 
> 	/**
> 	 * Answer the TableAlias for aPath or aUserAlias
> 	 * @param aPath
> 	 * @param aUserAlias
> 	 * @param anOriginalPath
> 	 * @return TableAlias, null if none
> 	 */
> 	private TableAlias getTableAliasForPath(String aPath, String aUserAlias, String anOriginalPath)
> 	{
> 		// PAW
> 		//if (aUserAlias == null || !anOriginalPath.equals(aPath))
> 		if (aUserAlias == null)
> 		{
> 			return getTableAliasForPath(aPath);
> 		}
> 		else
> 		{
> 			// PAW
> 			//return getTableAliasForPath(aUserAlias);
> 			return getTableAliasForPath(aUserAlias+aPath);
> 		}
> 	}
> 
> 	/**
> 	 * TODO: add super ClassDescriptor
> 	 * answer the ClassDescriptor for itemClass for an ObjectReferenceDescriptor
> 	 * check optional hint;
> 	 */
> 	// PAW
> //	private ClassDescriptor getItemClassDescriptor(ObjectReferenceDescriptor ord, String attr)
> 	private ClassDescriptor getItemClassDescriptor(ObjectReferenceDescriptor ord, String attr, Map pathClasses)
> 	{
> 		// PAW
> 		//List itemClasses = null;
> //		  Query q = getQuery();
> //
> //		  if (q instanceof QueryByCriteria)
> //		  {
> //			  itemClasses = ((QueryByCriteria) q).getClassesForPath(attr);
> //		  }
> 		List itemClasses = (List)pathClasses.get(attr);
> 
> 		if (itemClasses == null)
> 		{
> 			itemClasses = new ArrayList();
> 			itemClasses.add(ord.getItemClass());
> 		}
> 
> 		List classDescriptors = new ArrayList(itemClasses.size());
> 		DescriptorRepository repo = ord.getClassDescriptor().getRepository();
> 
> 		for (Iterator iter = itemClasses.iterator(); iter.hasNext();)
> 		{
> 			Class clazz = (Class) iter.next();
> 			classDescriptors.add(repo.getDescriptorFor(clazz));
> 		}
> 		return (ClassDescriptor) classDescriptors.get(0);
> 
> 	}
> 
> 
> 	/**
> 	 * Appends to the statement the ORDER BY clause for the Query
> 	 */
> 	protected void appendOrderByClause(List orderByFields, int[] orderByColumnNumbers, StringBuffer buf)
> 	{
> 		FieldHelper cf;
> 
> 		if (orderByColumnNumbers == null)
> 		{
> 			return;
> 		}
> 
> 		buf.append(" ORDER BY ");
> 		for (int i = 0; i < orderByColumnNumbers.length; i++)
> 		{
> 			cf = (FieldHelper) orderByFields.get(i);
> 			if (i > 0)
> 			{
> 				buf.append(",");
> 			}
> 			buf.append(orderByColumnNumbers[i]);
> 			if (!cf.isAscending)
> 			{
> 				buf.append(" DESC");
> 			}
> 		}
> 	}
> 
> 	/**
> 	 * Appends to the statement the GROUP BY clause for the Query
> 	 */
> 	protected void appendGroupByClause(List groupByFields, StringBuffer buf)
> 	{
> 		FieldHelper cf;
> 
> 		if (groupByFields == null || groupByFields.size() == 0)
> 		{
> 			return;
> 		}
> 
> 		buf.append(" GROUP BY ");
> 		for (int i = 0; i < groupByFields.size(); i++)
> 		{
> 			cf = (FieldHelper) groupByFields.get(i);
> 			if (i > 0)
> 			{
> 				buf.append(",");
> 			}
> 			appendColName(cf.name, false, null, buf);
> 		}
> 	}
> 
> 	/**
> 	 * Appends to the statement table and all tables joined to it.
> 	 * @param alias the table alias
> 	 * @param where append conditions for WHERE clause here
> 	 */
> 	protected void appendTableWithJoins(TableAlias alias, StringBuffer where, StringBuffer buf)
> 	{
> 		int stmtFromPos = 0;
> 		byte joinSyntax = getJoinSyntaxType();
> 
> 		if (joinSyntax == SQL92_JOIN_SYNTAX)
> 		{
> 			stmtFromPos = buf.length(); // store position of join (by: Terry Dexter)
> 		}
> 
> 		if (!(joinSyntax == SQL92_NOPAREN_JOIN_SYNTAX && alias != getRoot()))
> 		{
> 			buf.append(alias.getTableAndAlias());
> 
> 			if (getQuery() instanceof MtoNQuery)
> 			{
> 				buf.append(",");
> 				buf.append(((MtoNQuery) getQuery()).getIndirectionTable());
> 			}
> 		}
> 
> 		if (!alias.hasJoins())
> 		{
> 			return;
> 		}
> 
> 		for (Iterator it = alias.iterateJoins(); it.hasNext();)
> 		{
> 			Join join = (Join) it.next();
> 
> 			if (joinSyntax == SQL92_JOIN_SYNTAX)
> 			{
> 				appendJoinSQL92(join, where, buf);
> 				if (it.hasNext())
> 				{
> 					buf.insert(stmtFromPos, "(");
> 					buf.append(")");
> 				}
> 			}
> 			else if (joinSyntax == SQL92_NOPAREN_JOIN_SYNTAX)
> 			{
> 				appendJoinSQL92NoParen(join, where, buf);
> 			}
> 			else
> 			{
> 				appendJoin(where, buf, join);
> 			}
> 
> 		}
> 	}
> 
> 	/**
> 	 * Append Join for non SQL92 Syntax
> 	 */
> 	private void appendJoin(StringBuffer where, StringBuffer buf, Join join)
> 	{
> 		buf.append(",");
> 		appendTableWithJoins(join.right, where, buf);
> 		if (where.length() > 0)
> 		{
> 			where.append(" AND ");
> 		}
> 		join.appendJoinEqualities(where);
> 	}
> 
> 	/**
> 	 * Append Join for SQL92 Syntax
> 	 */
> 	private void appendJoinSQL92(Join join, StringBuffer where, StringBuffer buf)
> 	{
> 		if (join.isOuter)
> 		{
> 			buf.append(" LEFT OUTER JOIN ");
> 		}
> 		else
> 		{
> 			buf.append(" INNER JOIN ");
> 		}
> 		if (join.right.hasJoins())
> 		{
> 			buf.append("(");
> 			appendTableWithJoins(join.right, where, buf);
> 			buf.append(")");
> 		}
> 		else
> 		{
> 			appendTableWithJoins(join.right, where, buf);
> 		}
> 		buf.append(" ON ");
> 		join.appendJoinEqualities(buf);
> 	}
> 
> 	/**
> 	 * Append Join for SQL92 Syntax without parentheses
> 	 */
> 	private void appendJoinSQL92NoParen(Join join, StringBuffer where, StringBuffer buf)
> 	{
> 		if (join.isOuter)
> 		{
> 			buf.append(" LEFT OUTER JOIN ");
> 		}
> 		else
> 		{
> 			buf.append(" INNER JOIN ");
> 		}
> 
> 		buf.append(join.right.getTableAndAlias());
> 		buf.append(" ON ");
> 		join.appendJoinEqualities(buf);
> 
> 		appendTableWithJoins(join.right, where, buf);
> 	}
> 
> 	/**
> 	 * Appends to the statement columns if they are not found among the existingColumns.
> 	 * Columns added here use a column-alias "ojb_col_x", x being the number of existing columns
> 	 * @param columns the list of columns represented by Criteria.Field to ensure
> 	 * @param existingColumns the list of column names (String) that are already appended
> 	 * @return the array of column numbers (base 1)
> 	 */
> 	protected int[] ensureColumns(List columns, List existingColumns, StringBuffer buf)
> 	{
> 		FieldHelper cf;
> 		int[] columnNumbers = new int[columns.size()];
> 
> 		for (int i = 0; i < columnNumbers.length; i++)
> 		{
> 			cf = (FieldHelper) columns.get(i);
> 			columnNumbers[i] = existingColumns.indexOf(cf.name);
> 			if (columnNumbers[i] == -1)
> 			{
> 				columnNumbers[i] = existingColumns.size();
> 				existingColumns.add(cf.name);
> 				buf.append(",");
> 				appendColName(cf.name, "ojb_col_" + columnNumbers[i], false, null, buf);
> 			}
> 			columnNumbers[i]++; // columns numbers have base 1
> 		}
> 		return columnNumbers;
> 	}
> 
> 	/**
> 	 * Build the tree of joins for the given criteria
> 	 */
> 	private void buildJoinTree(Criteria crit)
> 	{
> 		Enumeration e = crit.getElements();
> 
> 		while (e.hasMoreElements())
> 		{
> 			Object o = e.nextElement();
> 			if (o instanceof Criteria)
> 			{
> 				buildJoinTree((Criteria) o);
> 			}
> 			else
> 			{
> 				SelectionCriteria c = (SelectionCriteria) o;
> 				
> 				// BRJ skip SqlCriteria
> 				if (c instanceof SqlCriteria)
> 				{
> 					continue;
> 				}				
> 				
> 				// BRJ: Outer join for OR
> 				boolean useOuterJoin = (crit.getType() == Criteria.OR);
> 
> 				// BRJ: do not build join tree for subQuery attribute                  
> 				if (c.getAttribute() != null && c.getAttribute() instanceof String)
> 				{
> 					// PAW
> 					//buildJoinTreeForColumn((String) c.getAttribute(), useOuterJoin, c.getAlias());
> 					buildJoinTreeForColumn((String) c.getAttribute(), useOuterJoin, c.getAlias(), c.getPathClasses());
> 				}
> 				if (c instanceof FieldCriteria)
> 				{
> 					FieldCriteria cc = (FieldCriteria) c;
> 					// PAW
> 					//buildJoinTreeForColumn((String) cc.getValue(), useOuterJoin, c.getAlias());
> 					buildJoinTreeForColumn((String) cc.getValue(), useOuterJoin, c.getAlias(), c.getPathClasses());
> 				}
> 			}
> 		}
> 	}
> 
> 	/**
> 	 * build the Join-Information for name
> 	 * functions and the last segment are removed
> 	 * ie: avg(accounts.amount) -> accounts
> 	 */
> 	// PAW
> 	//private void buildJoinTreeForColumn(String aColName, boolean useOuterJoin, String aUserAlias)
> 	private void buildJoinTreeForColumn(String aColName, boolean useOuterJoin, String aUserAlias, Map pathClasses)
> 	{
> 		String pathName = SqlHelper.cleanPath(aColName);
> 		int sepPos = pathName.lastIndexOf(".");
> 		
> 		if (sepPos >= 0)
> 		{
> 			// PAW
> 			//getTableAlias(pathName.substring(0, sepPos), useOuterJoin, aUserAlias, new String[]{pathName
> 			//		.substring(sepPos + 1)});
> 			getTableAlias(pathName.substring(0, sepPos), useOuterJoin, aUserAlias,
> 						  new String[]{pathName.substring(sepPos + 1)}, pathClasses);
> 		}
> 	}
> 
> 	/**
> 	 * build the Join-Information if a super reference exists
> 	 *
> 	 * @param left
> 	 * @param cld
> 	 * @param name
> 	 */
> 	protected void buildSuperJoinTree(TableAlias left, ClassDescriptor cld, String name)
> 	{
> 		Iterator objRefs = cld.getObjectReferenceDescriptors().iterator();
> 		while (objRefs.hasNext())
> 		{
> 			ObjectReferenceDescriptor objRef = (ObjectReferenceDescriptor) objRefs.next();
> 			FieldDescriptor[] leftFields = objRef.getForeignKeyFieldDescriptors(cld);
> 
> 			ClassDescriptor refCld = cld.getRepository().getDescriptorFor(objRef.getItemClassName());
> 			if (objRef.getPersistentField() instanceof AnonymousPersistentFieldForInheritance)
> 			{
> 				TableAlias base_alias = getTableAliasForPath(name, null);
> 
> 				String aliasName = String.valueOf(getAliasChar()) + m_aliasCount++;
> 				TableAlias right = new TableAlias(refCld, aliasName, false, null);
> 
> 				Join join1to1 = new Join(left, leftFields, right, refCld.getPkFields(), false, "superClass");
> 				base_alias.addJoin(join1to1);
> 
> 				buildSuperJoinTree(right, refCld, name);
> 			}
> 		}
> 	}
> 
> 	/**
> 	 * First reduce the Criteria to the normal disjunctive form, then
> 	 * calculate the necessary tree of joined tables for each item, then group
> 	 * items with the same tree of joined tables.
> 	 */
> 	protected void splitCriteria()
> 	{
> 		Criteria whereCrit = getQuery().getCriteria();
> 		Criteria havingCrit = getQuery().getHavingCriteria();
> 
> 		if (whereCrit == null || whereCrit.isEmpty())
> 		{
> 			getJoinTreeToCriteria().put(getRoot(), null);
> 		}
> 		else
> 		{
> 			// TODO: parameters list shold be modified when the form is reduced to DNF.
> 			getJoinTreeToCriteria().put(getRoot(), whereCrit);
> 			buildJoinTree(whereCrit);
> 		}
> 
> 		if (havingCrit == null || havingCrit.isEmpty())
> 		{
> 		}
> 		else
> 		{
> 			buildJoinTree(havingCrit);
> 		}
> 
> 	}
> 
> 	/**
> 	 * Gets the query.
> 	 * @return Returns a Query
> 	 */
> 	protected QueryByCriteria getQuery()
> 	{
> 		return m_query;
> 	}
> 
> 	/**
> 	 * Gets the root.
> 	 * @return Returns a TableAlias
> 	 */
> 	protected TableAlias getRoot()
> 	{
> 		return m_root;
> 	}
> 
> 	/**
> 	 * Sets the root.
> 	 * @param root The root to set
> 	 */
> 	protected void setRoot(TableAlias root)
> 	{
> 		this.m_root = root;
> 	}
> 
> 	/**
> 	 * Gets the search table of this query.
> 	 * @return Returns a TableAlias
> 	 */
> 	protected TableAlias getSearchTable()
> 	{
> 		return m_search;
> 	}
> 
> 	/**
> 	 * Gets the joinTreeToCriteria.
> 	 * @return Returns a HashMap
> 	 */
> 	protected HashMap getJoinTreeToCriteria()
> 	{
> 		return m_joinTreeToCriteria;
> 	}
> 
> 	/**
> 	 * Returns the joinSyntaxType.
> 	 * @return byte
> 	 */
> 	protected byte getJoinSyntaxType()
> 	{
> 		return m_platform.getJoinSyntaxType();
> 	}
> 
> 	/**
> 	 * Returns the logger.
> 	 * @return Logger
> 	 */
> 	protected Logger getLogger()
> 	{
> 		return m_logger;
> 	}
> 
> 	//-----------------------------------------------------------------
> 	// ------------------- Inner classes ------------------------------
> 	//-----------------------------------------------------------------
> 
> 	/**
> 	 * This class is a helper to return TableAlias and PathInfo
> 	 */
> 	static final class AttributeInfo
> 	{
> 		TableAlias tableAlias;
> 		PathInfo pathInfo;
> 	}
> 
> 	/**
> 	 * This class represents one table (possibly with alias) in the SQL query
> 	 */
> 	static final class TableAlias
> 	{
> 		Logger logger = LoggerFactory.getLogger(TableAlias.class);
> 		ClassDescriptor cld; // Is null for indirection table of M:N relation
> 		String table;
> 		final String alias;
> 		List extents = new ArrayList();
> 		List hints = new ArrayList();
> 		List joins;
> 
> 		TableAlias(String aTable, String anAlias)
> 		{
> 			this.cld = null;
> 			this.table = aTable;
> 			this.alias = anAlias;
> 		}
> 
> 		TableAlias(ClassDescriptor aCld, String anAlias)
> 		{
> 			this(aCld, anAlias, false, null);
> 		}
> 
> 		TableAlias(ClassDescriptor aCld, String anAlias, boolean lookForExtents, List hints)
> 		{
> 			this.cld = aCld;
> 			this.table = aCld.getFullTableName();
> 			this.alias = anAlias;
> 			boolean useHintsOnExtents = false;
> 
> 			//LEANDRO: use hints
> 			if (hints != null && hints.size() > 0)
> 			{
> 				useHintsOnExtents = true;
> 			}
> 
> 			logger.debug("TableAlias(): using hints ? " + useHintsOnExtents);
> 
> 			// BRJ : build alias for extents, only one per Table
> 			if (lookForExtents)
> 			{
> 				ClassDescriptor[] extCLDs = (ClassDescriptor[]) aCld.getRepository().getAllConcreteSubclassDescriptors(
> 						aCld).toArray(new ClassDescriptor[0]);
> 
> 				ClassDescriptor extCd;
> 				Class extClass;
> 				String extTable;
> 				Map extMap = new HashMap(); // only one Alias per Table
> 				int firtsNonAbstractExtentIndex = 0;
> 
> 				for (int i = 0; i < extCLDs.length; i++)
> 				{
> 					extCd = extCLDs[i];
> 					extClass = extCd.getClassOfObject();
> 					if (useHintsOnExtents && (!hints.contains(extClass)))
> 					{
> 						//LEANDRO: don't include this class
> 						logger.debug("Skipping class [" + extClass + "] from extents List");
> 						firtsNonAbstractExtentIndex++;
> 						continue;
> 					}
> 					extTable = extCd.getFullTableName();
> 
> 					// BRJ : Use the first non abstract extent
> 					// if the main cld is abstract
> 					//logger.debug("cld abstract["+aCld.isAbstract()+"] i["+i+"] index ["+firtsNonAbstractExtentIndex+"]");
> 					if (aCld.isAbstract() && i == firtsNonAbstractExtentIndex)
> 					{
> 						this.cld = extCd;
> 						this.table = extTable;
> 					}
> 					else
> 					{
> 						// Add a new extent entry only if the table of the extent
> 						// does not match the table of the 'base' class.
> 						if (extMap.get(extTable) == null && !extTable.equals(table))
> 						{
> 							extMap.put(extTable, new TableAlias(extCd, anAlias + "E" + i, false, hints));
> 						}
> 					}
> 				}
> 				extents.addAll(extMap.values());
> 			}
> 
> 			if (cld == null)
> 			{
> 				throw new PersistenceBrokerSQLException("Table is NULL for alias: " + alias);
> 			}
> 		}
> 
> 		ClassDescriptor getClassDescriptor()
> 		{
> 			return cld;
> 		}
> 
> 		String getTableAndAlias()
> 		{
> 			return table + " " + alias;
> 		}
> 
> 		boolean hasExtents()
> 		{
> 			return (!extents.isEmpty());
> 		}
> 
> 		Iterator iterateExtents()
> 		{
> 			return extents.iterator();
> 		}
> 
> 		/**
> 		 * Copy the Alias and all it's extents adding a Postfix
> 		 * Joins are not copied
> 		 * @param aPostfix
> 		 * @return
> 		 */
> 		TableAlias copy(String aPostfix)
> 		{
> 			TableAlias result, temp;
> 			Iterator iter = iterateExtents();
> 
> 			if (cld == null)
> 			{
> 				result = new TableAlias(table, alias + aPostfix);
> 			}
> 			else
> 			{
> 				result = new TableAlias(cld, alias + aPostfix);
> 			}
> 
> 			while (iter.hasNext())
> 			{
> 				temp = (TableAlias) iter.next();
> 				result.extents.add(temp.copy(aPostfix));
> 			}
> 
> 			return result;
> 		}
> 
> 		void addJoin(Join join)
> 		{
> 			if (joins == null)
> 			{
> 				joins = new ArrayList();
> 			}
> 			joins.add(join);
> 		}
> 
> 		Iterator iterateJoins()
> 		{
> 			return joins.iterator();
> 		}
> 
> 		boolean hasJoins()
> 		{
> 			return (joins != null);
> 		}
> 
> 		public String toString()
> 		{
> 			StringBuffer sb = new StringBuffer(1024);
> 			boolean first = true;
> 
> 			sb.append(getTableAndAlias());
> 			if (joins != null)
> 			{
> 				sb.append(" [");
> 				for (Iterator it = joins.iterator(); it.hasNext();)
> 				{
> 					Join join = (Join) it.next();
> 
> 					if (first)
> 					{
> 						first = false;
> 					}
> 					else
> 					{
> 						sb.append(", ");
> 					}
> 					sb.append("-(");
> 					sb.append(join.name);
> 					sb.append(")->");
> 					sb.append(join.right);
> 				}
> 				sb.append("]");
> 			}
> 			return sb.toString();
> 		}
> 
> 		public boolean equals(Object obj)
> 		{
> 			TableAlias t = (TableAlias) obj;
> 
> 			return table.equals(t.table); // BRJ: check table only
> 		}
> 
> 		public int hashCode()
> 		{
> 			return table.hashCode();
> 		}
> 
> 	}
> 
> 	/**
> 	 * This class represents join between two TableAliases
> 	 */
> 	final class Join
> 	{
> 		final TableAlias left;
> 		final String[] leftKeys;
> 		final TableAlias right;
> 		final String[] rightKeys;
> 		final boolean isOuter;
> 		/** This is the name of the field corresponding to this join */
> 		final String name;
> 
> 		/**
> 		 * leftKeys and rightKeys should be either FieldDescriptor[] or String[]
> 		 */
> 		Join(TableAlias left, Object[] leftKeys, TableAlias right, Object[] rightKeys, boolean isOuter, String name)
> 		{
> 			this.left = left;
> 			this.leftKeys = getColumns(leftKeys);
> 			this.right = right;
> 			this.rightKeys = getColumns(rightKeys);
> 			this.isOuter = isOuter;
> 			this.name = name;
> 		}
> 
> 		private String[] getColumns(Object[] keys)
> 		{
> 			String[] columns = new String[keys.length];
> 
> 			if (keys instanceof FieldDescriptor[])
> 			{
> 				FieldDescriptor[] kd = (FieldDescriptor[]) keys;
> 				for (int i = 0; i < columns.length; i++)
> 				{
> 					columns[i] = kd[i].getColumnName();
> 				}
> 			}
> 			else
> 			{
> 				for (int i = 0; i < columns.length; i++)
> 				{
> 					columns[i] = keys[i].toString();
> 				}
> 			}
> 			return columns;
> 		}
> 
> 		void appendJoinEqualities(StringBuffer buf)
> 		{
> 			byte joinSyntax = getJoinSyntaxType();
> 
> 			for (int i = 0; i < leftKeys.length; i++)
> 			{
> 				if (i > 0)
> 				{
> 					buf.append(" AND ");
> 				}
> 				buf.append(left.alias);
> 				buf.append(".");
> 				buf.append(leftKeys[i]);
> 
> 				if (isOuter && joinSyntax == SYBASE_JOIN_SYNTAX)
> 				{
> 					buf.append("*=");
> 				}
> 				else
> 				{
> 					buf.append("=");
> 				}
> 
> 				buf.append(right.alias);
> 				buf.append(".");
> 				buf.append(rightKeys[i]);
> 
> 				if (isOuter && joinSyntax == ORACLE_JOIN_SYNTAX)
> 				{
> 					buf.append("(+)");
> 				}
> 			}
> 		}
> 
> 		public boolean equals(Object obj)
> 		{
> 			Join j = (Join) obj;
> 			return name.equals(j.name) && (isOuter == j.isOuter) && right.equals(j.right);
> 		}
> 
> 		public int hashCode()
> 		{
> 			return name.hashCode();
> 		}
> 
> 		public String toString()
> 		{
> 			return left.alias + " -> " + right.alias;
> 		}
> 	}
> }
> 


---------------------------------------------------------------------
To unsubscribe, e-mail: ojb-user-unsubscribe@db.apache.org
For additional commands, e-mail: ojb-user-help@db.apache.org