Right, well it took about 5 hours longer than expected, what with 
downloading the new torque source, downloading the new maven, updating 
the jars, re-compiling my project, and then editing all the Vector/List 
changes, but I now have a set of patches that do a number of things:

1.  Add GROUP BY and HAVING functionality to the Criteria class through 
addHaving() and addGroupByColumn() methods
2.  Adjusts BasePeer so that databases that do not support native limit 
syntax, or have weird ones (like MSSQL) will still function with 
specified offset and limit parameters
3.  Adjusts the MSSQL adapter so that it says it can't support native 
limit operations and thus works with 2 above
4.  Makes SqlEnum a public class so that people can pass different 
comparators into Peer objects.

All the patches contain some chunks that will be rejected, but 
everything seems to work.  I think the rejects are due to coding style 
conflicts in the patch I originally submitted to solve the precedence 
problem in Criterion, that were then adjusted when Eric put the patches 
into cvs.

Oh yes, and my criterion string parsing classes that are used by the 
CriteriaTest are now in a separate file CriterionParser.java - included 
for your amusement.

I think the changes are fairly self-explanatory from the code, but 
please ask away if there are any questions.  All of the above have been 
checked on MySQL  3.23.47-nt  and SQLServer version 7.0

Now on to the actual work I was going to do today .....
Index: src/java/org/apache/torque/adapter/DBMSSQL.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-turbine-torque/src/java/org/apache/torque/adapter/DBMSSQL.java,v
retrieving revision 1.4
diff -u -r1.4 DBMSSQL.java
--- src/java/org/apache/torque/adapter/DBMSSQL.java     12 Apr 2002 05:58:32 -0000     
 1.4
+++ src/java/org/apache/torque/adapter/DBMSSQL.java     3 May 2002 06:45:42 -0000
@@ -73,4 +73,15 @@
     protected DBMSSQL()
     {
     }
+
+    /**
+     * This method is used to chek whether the database natively
+     * supports limiting the size of the resultset.
+     *
+     * @return True.
+     */
+    public boolean supportsNativeLimit()
+    {
+        return false;
+    }
 }
Index: src/java/org/apache/torque/util/SqlEnum.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-turbine-torque/src/java/org/apache/torque/util/SqlEnum.java,v
retrieving revision 1.4
diff -u -r1.4 SqlEnum.java
--- src/java/org/apache/torque/util/SqlEnum.java        20 Jan 2002 21:37:56 -0000     
 1.4
+++ src/java/org/apache/torque/util/SqlEnum.java        3 May 2002 02:06:43 -0000
@@ -64,7 +64,7 @@
  * @version $Id: SqlEnum.java,v 1.4 2002/01/20 21:37:56 jon Exp $
  * @since 3.0
  */
-class SqlEnum implements java.io.Serializable
+public class SqlEnum implements java.io.Serializable
 {
     private final String s;
     
package org.apache.torque.util;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001-2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *    "Apache Turbine" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache",
 *    "Apache Turbine", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import org.apache.torque.Torque;
import org.apache.torque.TorqueException;
import org.apache.torque.adapter.DB;
import org.apache.torque.map.DatabaseMap;
import org.apache.torque.map.TableMap;
import org.apache.torque.om.DateKey;
import org.apache.torque.om.ObjectKey;
import org.apache.torque.util.BasePeer;
import org.apache.commons.collections.StringStack;
import org.apache.log4j.Category;
import org.apache.torque.util.Criteria.Criterion;
import org.apache.torque.util.SqlEnum;

/**
 * This is a utility class that can translate string representations
 * of criterion clauses back into criterion clauses.
 *
 * NOTE: other methods will be added as needed and as time permits.
 *
 * @author <a href="mailto:[EMAIL PROTECTED]";>Sam Joseph</a>
 * @version $Id: $
 */
public class CriterionParser extends Criteria
{

    private Hashtable x_clause_table = new Hashtable();

    /**
     * This function takes a string representation of multiple Criterions
     * and turns it into a set of nested criterion objects
     *
     * @param p_criterion_string     Criterion String representation we want to parse
     * @return Criterion             Criterion object containing all sub Criterion 
objects
     */
    public Criterion parseCriterionString(String p_criterion_string)
      throws Exception
    {
        // find the first closing bracket
        int x_first_close = p_criterion_string.indexOf(')');
        if(x_first_close == -1) throw new Exception("no closing brace");
        int x_latest = -1;
        int x_matching_open = 0;
        
        // find the opening bracket in front of the closing bracket
        
        while(x_matching_open < x_first_close && x_matching_open != -1)
        {
          x_latest = x_matching_open;   
          x_matching_open = p_criterion_string.indexOf('(',x_latest+1);

        }
        if(x_latest == -1) throw new Exception("no opening brace");
        
        // extract the internal criterion pair in between the brackets
        
        String x_pair = p_criterion_string.substring(x_latest+1,x_first_close);
        boolean x_or = false;
        
        // work out what kind of clause it is
        
        int x_connector_start = x_pair.indexOf(Criterion.AND); 
        int x_connector_end = -1;
        if(x_connector_start == -1)
        {
          x_or = true;
          x_connector_start = x_pair.indexOf(Criterion.OR);
          if(x_connector_start == -1) 
            throw new Exception("no connecting element: " + x_pair);
          x_connector_end = x_connector_start + Criterion.OR.length();
        }
        else
          x_connector_end = x_connector_start + Criterion.AND.length();
          
        // work out start position of former criterion  
        
        char x_char = '?';
        int x_former_start = x_latest;
        int x_incrementer = x_latest;
        while(x_char == '?')
        {
          x_char = p_criterion_string.charAt(++x_incrementer);
          x_former_start++;
        }
        
        // see if we have already parsed this criterion or not 
        
        Integer x_position = new Integer(x_former_start);
        Criterion x_former = (Criterion)(x_clause_table.get(x_position));
        if(x_former == null)
        {
          x_former = parseCriterion(x_pair.substring(0,x_connector_start));
          x_clause_table.put(x_position,x_former);
        }
        
        // see if we have already parsed the latter criterion or not 
        
        x_position = new Integer(x_latest+1+x_connector_end+1);
        Criterion x_latter = (Criterion)(x_clause_table.get(x_position));
        if(x_latter == null)
        {
          x_latter = parseCriterion(x_pair.substring(x_connector_end));
          x_clause_table.put(x_position,x_latter);
        }
        
        Vector x_v = null;
        
        if(x_or == true)
          x_former.or(x_latter);
        else
          x_former.and(x_latter);
          
        // now take original string and remove latter Criterion
        
        char[] x_array = new char[p_criterion_string.length()];
          
        p_criterion_string.getChars(0, x_latest, x_array, 0); 
        x_array[x_latest] = '?';
        
        int x_end_former = x_latest+1+x_connector_start;
        
        p_criterion_string.getChars(x_latest+1,x_end_former,x_array,x_latest+1); 
        for(int i=x_latest+1+x_connector_start+1;i<x_first_close+1;i++)
          x_array[i] = '?';
          
        int x_end_string = p_criterion_string.length();
        int x_end_pair = x_first_close+1;
        p_criterion_string.getChars(x_end_pair, x_end_string, x_array, x_end_pair); 
        String x_new_string = new String(x_array);
        if(x_new_string.indexOf('(') == -1 && x_new_string.indexOf(')') == -1) 
          return x_former;  // if the remaining string has no brackets return criterion
        else
          return parseCriterionString(x_new_string); // otherwise delve deeper
    }
    
    /**
     * This function takes in a string representation of a single Criterion with
     * no brackets, and returns a Criterion object for that string
     *
     *@param p_criterion_string       Criterion string representation
     *@return Criterion               the corresponding Criterion object
     */
    public Criterion parseCriterion(String p_criterion_string)
      throws Exception
    {
        String x_table = null;
        
        // find the dot marking the end of the table name
        
        int x_end_table = p_criterion_string.indexOf('.');
        if(x_end_table!=-1) 
          x_table = p_criterion_string.substring(0,x_end_table);
        
        SqlEnum x_sql_enum = EQUAL;
        
        // determine what kind of comparator is being used
        // apologies for heavy nesting here
        // any suggestions for improvement gratefully recieved
        
        int x_end_column = p_criterion_string.indexOf(EQUAL.toString());
        if(x_end_column==-1) 
        {
          x_end_column = p_criterion_string.indexOf(NOT_EQUAL.toString());
          if(x_end_column==-1) 
          {
            x_end_column = p_criterion_string.indexOf(ALT_NOT_EQUAL.toString());
            if(x_end_column==-1) 
            {
              x_end_column = p_criterion_string.indexOf(GREATER_THAN.toString());
              if(x_end_column==-1) 
              {
                x_end_column = p_criterion_string.indexOf(LESS_THAN.toString());
                if(x_end_column==-1) 
                {
                  x_end_column = p_criterion_string.indexOf(GREATER_EQUAL.toString());
                  if(x_end_column==-1) 
                  {
                    x_end_column = p_criterion_string.indexOf(LESS_EQUAL.toString());
                    if(x_end_column==-1) 
                    {
                      x_end_column = p_criterion_string.indexOf(LIKE.toString());
                      if(x_end_column==-1) 
                      { 
                        x_end_column = p_criterion_string.indexOf(NOT_LIKE.toString());
                        if(x_end_column==-1) 
                        {
                          x_end_column = p_criterion_string.indexOf(IN.toString());
                          if(x_end_column==-1) 
                          {
                            x_end_column = 
p_criterion_string.indexOf(NOT_IN.toString());
                            if(x_end_column==-1) 
                            {
                              x_end_column = 
p_criterion_string.indexOf(ISNULL.toString());
                              if(x_end_column==-1)
                              { 
                                x_end_column = 
p_criterion_string.indexOf(ISNOTNULL.toString());
                                if(x_end_column==-1)       
                                {
                                  throw new Exception("No recognised comparator in 
this criterion: " 
                                                       + 
p_criterion_string.toString());
                                }
                                else
                                  x_sql_enum = ISNOTNULL;     
                              }
                              else
                              {
                                x_sql_enum = ISNULL;
                              }
                            }
                            else
                            {
                              x_sql_enum = NOT_IN;
                            }
                          }
                          else
                          {
                            x_sql_enum = IN;
                          }
                        }
                        else
                        {
                          x_sql_enum = NOT_LIKE;
                        }
                      }
                      else
                      {
                        x_sql_enum = LIKE;
                      }
                    }
                    else
                    {
                      x_sql_enum = LESS_EQUAL;  
                    }              
                  }
                  else
                  {
                    x_sql_enum = GREATER_EQUAL;               
                  }
                }
                else
                {
                  x_sql_enum = LESS_THAN;
                }
              }
              else
              {
                x_sql_enum = GREATER_THAN;
              }
            }
            else
            {
              x_sql_enum = ALT_NOT_EQUAL;
            }
          }
          else
          {
            x_sql_enum = NOT_EQUAL;
          }
        }
        else
        {
          x_sql_enum = EQUAL;
        }
        
        // extract the various components
        
        if(x_end_table == -1) x_end_table++;
        String x_column = p_criterion_string.substring(x_end_table+1,x_end_column);
        String x_value = 
p_criterion_string.substring(x_end_column+x_sql_enum.toString().length());
        x_value = x_value.replace('\'',' ');
        x_value = x_value.trim();
        
        // try and create the Criterion object
        
        Criterion x_criterion = null;
        if(x_table != null)
          x_criterion = new Criterion(x_table,x_column,(Object)(x_value),x_sql_enum);
        else
          x_criterion = new Criterion(x_column,(Object)(x_value),x_sql_enum);
          
        return x_criterion;
    }
}
--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to