Coverage report

  %line %branch
org.apache.torque.util.SqlExpression
64% 
89% 

 1  
 package org.apache.torque.util;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import java.lang.reflect.Array;
 23  
 import java.util.Date;
 24  
 import java.util.HashSet;
 25  
 import java.util.Iterator;
 26  
 import java.util.List;
 27  
 
 28  
 import org.apache.commons.lang.StringUtils;
 29  
 import org.apache.torque.TorqueException;
 30  
 import org.apache.torque.adapter.DB;
 31  
 import org.apache.torque.om.DateKey;
 32  
 import org.apache.torque.om.ObjectKey;
 33  
 import org.apache.torque.om.StringKey;
 34  
 
 35  
 
 36  
 /**
 37  
  * This class represents a part of an SQL query found in the <code>WHERE</code>
 38  
  * section.  For example:
 39  
  * <pre>
 40  
  * table_a.column_a = table_b.column_a
 41  
  * column LIKE 'F%'
 42  
  * table.column < 3
 43  
  * </pre>
 44  
  * This class is used primarily by {@link org.apache.torque.util.BasePeer}.
 45  
  *
 46  
  * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
 47  
  * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
 48  
  * @author <a href="mailto:fedor@apache.org">Fedor Karpelevitch</a>
 49  
  * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
 50  
  * @version $Id: SqlExpression.java 476550 2006-11-18 16:08:37Z tfischer $
 51  
  */
 52  
 public final class SqlExpression
 53  
 {
 54  
     /** escaped single quote */
 55  
     private static final char SINGLE_QUOTE = '\'';
 56  
     /** escaped backslash */
 57  
     private static final char BACKSLASH = '\\';
 58  
 
 59  
     /**
 60  
      * Private constructor to prevent instantiation.
 61  
      *
 62  
      * Class contains only static method ans should therefore not be
 63  
      * instantiated.
 64  
      */
 65  
     private SqlExpression()
 66  0
     {
 67  0
     }
 68  
 
 69  
     /**
 70  
      * Used to specify a join on two columns.
 71  
      *
 72  
      * @param column A column in one of the tables to be joined.
 73  
      * @param relatedColumn The column in the other table to be joined.
 74  
      * @return A join expression, e.g. UPPER(table_a.column_a) =
 75  
      *         UPPER(table_b.column_b).
 76  
      */
 77  
     public static String buildInnerJoin(String column, String relatedColumn)
 78  
     {
 79  
         // 'db' can be null because 'ignoreCase' is false.
 80  23
         return buildInnerJoin(column, relatedColumn, false, null);
 81  
     }
 82  
 
 83  
     /**
 84  
      * Used to specify a join on two columns.
 85  
      *
 86  
      * @param column A column in one of the tables to be joined.
 87  
      * @param relatedColumn The column in the other table to be joined.
 88  
      * @param ignoreCase If true and columns represent Strings, the appropriate
 89  
      *        function defined for the database will be used to ignore
 90  
      *        differences in case.
 91  
      * @param db Represents the database in use for vendor-specific functions.
 92  
      * @return A join expression, e.g. UPPER(table_a.column_a) =
 93  
      *         UPPER(table_b.column_b).
 94  
      */
 95  
     public static String buildInnerJoin(String column,
 96  
                                          String relatedColumn,
 97  
                                          boolean ignoreCase,
 98  
                                          DB db)
 99  
     {
 100  69
         int addlength = (ignoreCase) ? 25 : 1;
 101  72
         StringBuffer sb = new StringBuffer(column.length()
 102  3
                 + relatedColumn.length() + addlength);
 103  69
         buildInnerJoin(column, relatedColumn, ignoreCase, db, sb);
 104  69
         return sb.toString();
 105  
     }
 106  
 
 107  
     /**
 108  
      * Used to specify a join on two columns.
 109  
      *
 110  
      * @param column A column in one of the tables to be joined.
 111  
      * @param relatedColumn The column in the other table to be joined.
 112  
      * @param ignoreCase If true and columns represent Strings, the appropriate
 113  
      *        function defined for the database will be used to ignore
 114  
      *        differences in case.
 115  
      * @param db Represents the database in use for vendor-specific functions.
 116  
      * @param whereClause A StringBuffer to which the sql expression will be
 117  
      *        appended.
 118  
      */
 119  
     public static void buildInnerJoin(String column,
 120  
                                        String relatedColumn,
 121  
                                        boolean ignoreCase,
 122  
                                        DB db,
 123  
                                        StringBuffer whereClause)
 124  
     {
 125  69
         if (ignoreCase)
 126  
         {
 127  23
             whereClause.append(db.ignoreCase(column))
 128  1
                     .append('=')
 129  1
                     .append(db.ignoreCase(relatedColumn));
 130  22
         }
 131  
         else
 132  
         {
 133  46
             whereClause.append(column)
 134  2
                     .append('=')
 135  2
                     .append(relatedColumn);
 136  
         }
 137  69
     }
 138  
 
 139  
 
 140  
     /**
 141  
      * Builds a simple SQL expression.
 142  
      *
 143  
      * @param columnName A column.
 144  
      * @param criteria The value to compare the column against.
 145  
      * @param comparison One of =, &lt;, &gt;, ^lt;=, &gt;=, &lt;&gt;,
 146  
      *        !=, LIKE, etc.
 147  
      * @return A simple SQL expression, e.g. UPPER(table_a.column_a)
 148  
      *         LIKE UPPER('ab%c').
 149  
      * @throws TorqueException Any exceptions caught during processing will be
 150  
      *         rethrown wrapped into a TorqueException.
 151  
      */
 152  
     public static String build(String columnName,
 153  
                                 Object criteria,
 154  
                                 SqlEnum comparison)
 155  
         throws TorqueException
 156  
     {
 157  
         // 'db' can be null because 'ignoreCase' is null
 158  0
         return build(columnName, criteria, comparison, false, null);
 159  
     }
 160  
 
 161  
     /**
 162  
      * Builds a simple SQL expression.
 163  
      *
 164  
      * @param columnName A column.
 165  
      * @param criteria The value to compare the column against.
 166  
      * @param comparison One of =, &lt;, &gt;, ^lt;=, &gt;=, &lt;&gt;,
 167  
      *        !=, LIKE, etc.
 168  
      * @param ignoreCase If true and columns represent Strings, the appropriate
 169  
      *        function defined for the database will be used to ignore
 170  
      *        differences in case.
 171  
      * @param db Represents the database in use, for vendor specific functions.
 172  
      * @return A simple sql expression, e.g. UPPER(table_a.column_a)
 173  
      *         LIKE UPPER('ab%c').
 174  
      * @throws TorqueException Any exceptions caught during processing will be
 175  
      *         rethrown wrapped into a TorqueException.
 176  
      */
 177  
     public static String build(String columnName,
 178  
                                 Object criteria,
 179  
                                 SqlEnum comparison,
 180  
                                 boolean ignoreCase,
 181  
                                 DB db)
 182  
         throws TorqueException
 183  
     {
 184  0
         int addlength = (ignoreCase ? 40 : 20);
 185  0
         StringBuffer sb = new StringBuffer(columnName.length() + addlength);
 186  0
         build(columnName, criteria, comparison, ignoreCase, db, sb);
 187  0
         return sb.toString();
 188  
     }
 189  
 
 190  
     /**
 191  
      * Builds a simple SQL expression.
 192  
      *
 193  
      * @param columnName A column.
 194  
      * @param criteria The value to compare the column against.
 195  
      * @param comparison One of =, &lt;, &gt;, ^lt;=, &gt;=, &lt;&gt;,
 196  
      *        !=, LIKE, etc.
 197  
      * @param ignoreCase If true and columns represent Strings, the appropriate
 198  
      *        function defined for the database will be used to ignore
 199  
      *        differences in case.
 200  
      * @param db Represents the database in use, for vendor specific functions.
 201  
      * @param whereClause A StringBuffer to which the sql expression will be
 202  
      *        appended.
 203  
      */
 204  
     public static void build(String columnName,
 205  
                               Object criteria,
 206  
                               SqlEnum comparison,
 207  
                               boolean ignoreCase,
 208  
                               DB db,
 209  
                               StringBuffer whereClause)
 210  
             throws TorqueException
 211  
     {
 212  
         // Allow null criteria
 213  
         // This will result in queries like
 214  
         // insert into table (name, parent) values ('x', null);
 215  
         //
 216  
 
 217  
         /* Check to see if the criteria is an ObjectKey
 218  
          * and if the value of that ObjectKey is null.
 219  
          * In that case, criteria should be null.
 220  
          */
 221  
 
 222  1012
         if (criteria != null && criteria instanceof ObjectKey)
 223  
         {
 224  0
             if (((ObjectKey) criteria).getValue() == null)
 225  
             {
 226  0
                 criteria = null;
 227  
             }
 228  
         }
 229  
         /*  If the criteria is null, check to see comparison
 230  
          *  is an =, <>, or !=.  If so, replace the comparison
 231  
          *  with the proper IS or IS NOT.
 232  
          */
 233  
 
 234  1012
         if (criteria == null)
 235  
         {
 236  0
             criteria = "null";
 237  0
             if (comparison.equals(Criteria.EQUAL))
 238  
             {
 239  0
                 comparison = Criteria.ISNULL;
 240  0
             }
 241  0
             else if (comparison.equals(Criteria.NOT_EQUAL))
 242  
             {
 243  0
                 comparison = Criteria.ISNOTNULL;
 244  0
             }
 245  0
             else if (comparison.equals(Criteria.ALT_NOT_EQUAL))
 246  
             {
 247  0
                 comparison = Criteria.ISNOTNULL;
 248  0
             }
 249  
         }
 250  
         else
 251  
         {
 252  1012
             if (criteria instanceof String || criteria instanceof StringKey)
 253  
             {
 254  552
                 criteria = quoteAndEscapeText(criteria.toString(), db);
 255  528
             }
 256  460
             else if (criteria instanceof Date)
 257  
             {
 258  92
                 Date dt = (Date) criteria;
 259  92
                 criteria = db.getDateString(dt);
 260  88
             }
 261  368
             else if (criteria instanceof DateKey)
 262  
             {
 263  0
                 Date dt = (Date) ((DateKey) criteria).getValue();
 264  0
                 criteria = db.getDateString(dt);
 265  0
             }
 266  368
             else if (criteria instanceof Boolean)
 267  
             {
 268  46
                 criteria = db.getBooleanString((Boolean) criteria);
 269  44
             }
 270  322
             else if (criteria instanceof Criteria)
 271  
             {
 272  0
                  Query subquery = SQLBuilder.buildQueryClause(
 273  
                         (Criteria) criteria,
 274  
                         null,
 275  
                         new SQLBuilder.QueryCallback() {
 276  
                             public String process(
 277  
                                     Criteria.Criterion criterion,
 278  
                                     List params)
 279  
                             {
 280  
                                 return criterion.toString();
 281  
                             }
 282  
                 });
 283  0
                 if (comparison.equals(Criteria.IN)
 284  
                         || comparison.equals(Criteria.NOT_IN))
 285  
                 {
 286  
                     // code below takes care of adding brackets
 287  0
                     criteria = subquery.toString();
 288  0
                 }
 289  
                 else
 290  
                 {
 291  0
                     criteria = "(" + subquery.toString() + ")";
 292  
                 }
 293  
             }
 294  
         }
 295  
 
 296  1012
         if (comparison.equals(Criteria.LIKE)
 297  41
                 || comparison.equals(Criteria.NOT_LIKE)
 298  40
                 || comparison.equals(Criteria.ILIKE)
 299  40
                 || comparison.equals(Criteria.NOT_ILIKE))
 300  
         {
 301  96
             buildLike(columnName, (String) criteria, comparison,
 302  4
                        ignoreCase, db, whereClause);
 303  88
         }
 304  920
         else if (comparison.equals(Criteria.IN)
 305  40
                 || comparison.equals(Criteria.NOT_IN))
 306  
         {
 307  0
             buildIn(columnName, criteria, comparison,
 308  
                      ignoreCase, db, whereClause);
 309  0
         }
 310  
         else
 311  
         {
 312  
             // Do not put the upper/lower keyword around IS NULL
 313  
             //  or IS NOT NULL
 314  920
             if (comparison.equals(Criteria.ISNULL)
 315  40
                     || comparison.equals(Criteria.ISNOTNULL))
 316  
             {
 317  0
                 whereClause.append(columnName)
 318  
                         .append(comparison);
 319  0
             }
 320  
             else
 321  
             {
 322  920
                 String columnValue = criteria.toString();
 323  920
                 if (ignoreCase && db != null)
 324  
                 {
 325  0
                     columnName = db.ignoreCase(columnName);
 326  0
                     columnValue = db.ignoreCase(columnValue);
 327  
                 }
 328  920
                 whereClause.append(columnName)
 329  40
                         .append(comparison)
 330  40
                         .append(columnValue);
 331  
             }
 332  
         }
 333  1012
     }
 334  
 
 335  
     /**
 336  
      * Takes a columnName and criteria and builds an SQL phrase based
 337  
      * on whether wildcards are present and the state of the
 338  
      * ignoreCase flag.  Multicharacter wildcards % and * may be used
 339  
      * as well as single character wildcards, _ and ?.  These
 340  
      * characters can be escaped with \.
 341  
      *
 342  
      * e.g. criteria = "fre%" -> columnName LIKE 'fre%'
 343  
      *                        -> UPPER(columnName) LIKE UPPER('fre%')
 344  
      *      criteria = "50\%" -> columnName = '50%'
 345  
      *
 346  
      * @param columnName A column.
 347  
      * @param criteria The value to compare the column against.
 348  
      * @param comparison Whether to do a LIKE or a NOT LIKE
 349  
      * @param ignoreCase If true and columns represent Strings, the
 350  
      * appropriate function defined for the database will be used to
 351  
      * ignore differences in case.
 352  
      * @param db Represents the database in use, for vendor specific functions.
 353  
      * @return An SQL expression.
 354  
      */
 355  
     static String buildLike(String columnName,
 356  
                              String criteria,
 357  
                              SqlEnum comparison,
 358  
                              boolean ignoreCase,
 359  
                              DB db)
 360  
     {
 361  46
         StringBuffer whereClause = new StringBuffer();
 362  48
         buildLike(columnName, criteria, comparison, ignoreCase, db,
 363  2
                    whereClause);
 364  46
         return whereClause.toString();
 365  
     }
 366  
 
 367  
     /**
 368  
      * Takes a columnName and criteria and builds an SQL phrase based
 369  
      * on whether wildcards are present and the state of the
 370  
      * ignoreCase flag.  Multicharacter wildcards % and * may be used
 371  
      * as well as single character wildcards, _ and ?.  These
 372  
      * characters can be escaped with \.
 373  
      *
 374  
      * e.g. criteria = "fre%" -> columnName LIKE 'fre%'
 375  
      *                        -> UPPER(columnName) LIKE UPPER('fre%')
 376  
      *      criteria = "50\%" -> columnName = '50%'
 377  
      *
 378  
      * @param columnName A column name.
 379  
      * @param criteria The value to compare the column against.
 380  
      * @param comparison Whether to do a LIKE or a NOT LIKE
 381  
      * @param ignoreCase If true and columns represent Strings, the
 382  
      * appropriate function defined for the database will be used to
 383  
      * ignore differences in case.
 384  
      * @param db Represents the database in use, for vendor specific functions.
 385  
      * @param whereClause A StringBuffer to which the sql expression
 386  
      * will be appended.
 387  
      */
 388  
     static void buildLike(String columnName,
 389  
                            String criteria,
 390  
                            SqlEnum comparison,
 391  
                            boolean ignoreCase,
 392  
                            DB db,
 393  
                            StringBuffer whereClause)
 394  
     {
 395  
         // If selection criteria contains wildcards use LIKE otherwise
 396  
         // use = (equals).  Wildcards can be escaped by prepending
 397  
         // them with \ (backslash). However, if we switch from
 398  
         // like to equals, we need to remove the escape characters.
 399  
         // from the wildcards.
 400  
         // So we need two passes: The first replaces * and ? by % and _,
 401  
         // and checks whether we switch to equals,
 402  
         // the second removes escapes if we have switched to equals.
 403  138
         int position = 0;
 404  138
         StringBuffer sb = new StringBuffer();
 405  138
         boolean replaceWithEquals = true;
 406  1317
         while (position < criteria.length())
 407  
         {
 408  1173
             char checkWildcard = criteria.class="keyword">charAt(position);
 409  
 
 410  1173
             switch (checkWildcard)
 411  
             {
 412  
             case BACKSLASH:
 413  
                 // if text is escaped, all backslashes are already escaped,
 414  
                 // so the next character after the backslash is the doubled
 415  
                 // backslash from escaping.
 416  23
                 int charsToProceed = db.escapeText() ? 2 : 1;
 417  23
                 if (position + charsToProceed >= criteria.length())
 418  
                 {
 419  0
                     charsToProceed = criteria.length() - position - 1;
 420  0
                 }
 421  23
                 else if (criteria.charAt(position + charsToProceed) == BACKSLASH
 422  
                         && db.escapeText())
 423  
                 {
 424  
                     // the escaped backslash is also escaped,
 425  
                     // so we need to proceed another character
 426  0
                     charsToProceed += 1;
 427  
                 }
 428  24
                 sb.append(criteria.substring(
 429  1
                         position,
 430  1
                         position + charsToProceed));
 431  23
                 position += charsToProceed;
 432  
                 // code below copies escaped character into sb
 433  23
                 checkWildcard = criteria.charAt(position);
 434  23
                 break;
 435  
             case '%':
 436  
             case '_':
 437  23
                 replaceWithEquals = false;
 438  23
                 break;
 439  
             case '*':
 440  0
                 replaceWithEquals = false;
 441  0
                 checkWildcard = '%';
 442  0
                 break;
 443  
             case '?':
 444  0
                 replaceWithEquals = false;
 445  0
                 checkWildcard = '_';
 446  
                 break;
 447  
             }
 448  
 
 449  1173
             sb.append(checkWildcard);
 450  1173
             position++;
 451  1122
         }
 452  138
         criteria = sb.toString();
 453  
 
 454  138
         if (ignoreCase)
 455  
         {
 456  46
             if (db.useIlike() && !replaceWithEquals)
 457  
             {
 458  0
                 if (SqlEnum.LIKE.equals(comparison))
 459  
                 {
 460  0
                     comparison = SqlEnum.ILIKE;
 461  0
                 }
 462  0
                 else if (SqlEnum.NOT_LIKE.equals(comparison))
 463  
                 {
 464  0
                     comparison = SqlEnum.NOT_ILIKE;
 465  0
                 }
 466  
             }
 467  
             else
 468  
             {
 469  
                 // no native case insensitive like is offered by the DB,
 470  
                 // or the LIKE was replaced with equals.
 471  
                 // need to ignore case manually.
 472  46
                 columnName = db.ignoreCase(columnName);
 473  
             }
 474  
         }
 475  138
         whereClause.append(columnName);
 476  
 
 477  138
         if (replaceWithEquals)
 478  
         {
 479  115
             if (comparison.equals(Criteria.NOT_LIKE)
 480  4
                     || comparison.equals(Criteria.NOT_ILIKE))
 481  
             {
 482  23
                 whereClause.append(" ").append(Criteria.NOT_EQUAL).append(" ");
 483  22
             }
 484  
             else
 485  
             {
 486  92
                 whereClause.append(" ").append(Criteria.EQUAL).append(" ");
 487  
             }
 488  
 
 489  
             // remove escape backslashes from String
 490  115
             position = 0;
 491  115
             sb = new StringBuffer();
 492  1201
             while (position < criteria.length())
 493  
             {
 494  1081
                 char checkWildcard = criteria.class="keyword">charAt(position);
 495  
 
 496  1081
                 if (checkWildcard == BACKSLASH)
 497  
                 {
 498  
                     // if text is escaped, all backslashes are already escaped,
 499  
                     // so the next character after the backslash is the doubled
 500  
                     // backslash from escaping.
 501  23
                     int charsToSkip = db.escapeText() ? 2 : 1;
 502  23
                     if (position + charsToSkip >= criteria.length())
 503  
                     {
 504  0
                         charsToSkip = criteria.length() - position - 1;
 505  0
                     }
 506  24
                     else if (criteria.charAt(position + charsToSkip)
 507  1
                                 == BACKSLASH
 508  
                             && db.escapeText())
 509  
                     {
 510  
                         // the escaped backslash is also escaped,
 511  
                         // so we need to skip another character
 512  
                         // but add the escaped backslash to sb
 513  
                         // so that the escaping remains.
 514  0
                         sb.append(BACKSLASH);
 515  0
                         charsToSkip += 1;
 516  
                     }
 517  23
                     position += charsToSkip;
 518  
                     // code below copies escaped character into sb
 519  23
                     checkWildcard = criteria.charAt(position);
 520  
                 }
 521  1081
                 sb.append(checkWildcard);
 522  1081
                 position++;
 523  1034
             }
 524  115
             criteria = sb.toString();
 525  110
        }
 526  
         else
 527  
         {
 528  23
             whereClause.append(comparison);
 529  
         }
 530  
 
 531  
         // If selection is case insensitive use SQL UPPER() function
 532  
         // on criteria.
 533  138
         if (ignoreCase && (!(db.useIlike()) || replaceWithEquals))
 534  
         {
 535  46
             criteria = db.ignoreCase(criteria);
 536  
         }
 537  138
         whereClause.append(criteria);
 538  
 
 539  138
         if (!replaceWithEquals && db.useEscapeClauseForLike())
 540  
         {
 541  0
             whereClause.append(SqlEnum.ESCAPE)
 542  
                        .append("'\\'");
 543  
         }
 544  138
     }
 545  
 
 546  
     /**
 547  
      * Takes a columnName and criteria (which must be an array) and
 548  
      * builds a SQL 'IN' expression taking into account the ignoreCase
 549  
      * flag.
 550  
      *
 551  
      * @param columnName A column.
 552  
      * @param criteria The value to compare the column against.
 553  
      * @param comparison Either " IN " or " NOT IN ".
 554  
      * @param ignoreCase If true and columns represent Strings, the
 555  
      * appropriate function defined for the database will be used to
 556  
      * ignore differences in case.
 557  
      * @param db Represents the database in use, for vendor specific functions.
 558  
      * @return An SQL expression.
 559  
      */
 560  
     static String buildIn(String columnName,
 561  
                           Object criteria,
 562  
                           SqlEnum comparison,
 563  
                           boolean ignoreCase,
 564  
                           DB db)
 565  
     {
 566  46
         StringBuffer whereClause = new StringBuffer();
 567  48
         buildIn(columnName, criteria, comparison,
 568  2
                 ignoreCase, db, whereClause);
 569  46
         return whereClause.toString();
 570  
     }
 571  
 
 572  
     /**
 573  
      * Takes a columnName and criteria (which must be an array) and
 574  
      * builds a SQL 'IN' expression taking into account the ignoreCase
 575  
      * flag.
 576  
      *
 577  
      * @param columnName A column.
 578  
      * @param criteria The value to compare the column against.
 579  
      * @param comparison Either " IN " or " NOT IN ".
 580  
      * @param ignoreCase If true and columns represent Strings, the
 581  
      * appropriate function defined for the database will be used to
 582  
      * ignore differences in case.
 583  
      * @param db Represents the database in use, for vendor specific functions.
 584  
      * @param whereClause A StringBuffer to which the sql expression
 585  
      * will be appended.
 586  
      */
 587  
     static void buildIn(String columnName,
 588  
                         Object criteria,
 589  
                         SqlEnum comparison,
 590  
                         boolean ignoreCase,
 591  
                         DB db,
 592  
                         StringBuffer whereClause)
 593  
     {
 594  46
         if (ignoreCase)
 595  
         {
 596  46
             whereClause.append(db.ignoreCase(columnName));
 597  44
         }
 598  
         else
 599  
         {
 600  0
             whereClause.append(columnName);
 601  
         }
 602  
 
 603  46
         whereClause.append(comparison);
 604  46
         HashSet inClause = new HashSet();
 605  46
         if (criteria instanceof List)
 606  
         {
 607  0
             Iterator iter = ((List) criteria).iterator();
 608  0
             while (iter.hasNext())
 609  
             {
 610  0
                 Object value = iter.next();
 611  
 
 612  
                 // The method processInValue() quotes the string
 613  
                 // and/or wraps it in UPPER().
 614  0
                 inClause.add(processInValue(value, ignoreCase, db));
 615  0
             }
 616  0
         }
 617  46
         else if (criteria instanceof String)
 618  
         {
 619  
             // subquery
 620  0
             inClause.add(criteria);
 621  0
         }
 622  
         else
 623  
         {
 624  
             // Assume array.
 625  230115
             for (int i = 0; i < Array.getLength(criteria); i++)
 626  
             {
 627  230069
                 Object value = Array.get(criteria, i);
 628  
 
 629  
                 // The method processInValue() quotes the string
 630  
                 // and/or wraps it in UPPER().
 631  230069
                 inClause.add(processInValue(value, ignoreCase, db));
 632  
             }
 633  
         }
 634  46
         whereClause.append('(')
 635  2
                    .append(StringUtils.join(inClause.iterator(), ","))
 636  2
                    .append(')');
 637  46
     }
 638  
 
 639  
     /**
 640  
      * Creates an appropriate string for an 'IN' clause from an
 641  
      * object.  Adds quoting and/or UPPER() as appropriate.  This is
 642  
      * broken out into a seperate method as it is used in two places
 643  
      * in buildIn, depending on whether an array or List is being
 644  
      * looped over.
 645  
      *
 646  
      * @param value The value to process.
 647  
      * @param ignoreCase Coerce the value suitably for ignoring case.
 648  
      * @param db Represents the database in use for vendor specific functions.
 649  
      * @return Processed value as String.
 650  
      */
 651  
     static String processInValue(Object value,
 652  
                                  boolean ignoreCase,
 653  
                                  DB db)
 654  
     {
 655  230069
         String ret = null;
 656  230069
         if (value instanceof String)
 657  
         {
 658  230069
             ret = quoteAndEscapeText((String) value, db);
 659  220066
         }
 660  
         else
 661  
         {
 662  0
             ret = value.toString();
 663  
         }
 664  230069
         if (ignoreCase)
 665  
         {
 666  230069
             ret = db.ignoreCase(ret);
 667  
         }
 668  230069
         return ret;
 669  
     }
 670  
 
 671  
     /**
 672  
      * Quotes and escapes raw text for placement in a SQL expression.
 673  
      * For simplicity, the text is assumed to be neither quoted nor
 674  
      * escaped.
 675  
      *
 676  
      * @param rawText The <i>unquoted</i>, <i>unescaped</i> text to process.
 677  
      * @param db the db
 678  
      * @return Quoted and escaped text.
 679  
      */
 680  
     public static String quoteAndEscapeText(String rawText, DB db)
 681  
     {
 682  230621
         StringBuffer buf = new StringBuffer((int) (rawText.length() * 1.1));
 683  
 
 684  
         // Some databases do not need escaping.
 685  
         String escapeString;
 686  230621
         if (db != null && !db.escapeText())
 687  
         {
 688  0
             escapeString = String.valueOf(BACKSLASH);
 689  0
         }
 690  
         else
 691  
         {
 692  240648
             escapeString = String.valueOf(BACKSLASH)
 693  10027
                     + String.valueOf(BACKSLASH);
 694  
         }
 695  
 
 696  230621
         char[] data = rawText.toCharArray();
 697  230621
         buf.append(SINGLE_QUOTE);
 698  1129369
         for (int i = 0; i < data.length; i++)
 699  
         {
 700  898748
             switch (data[i])
 701  
             {
 702  
             case SINGLE_QUOTE:
 703  0
                 buf.append(SINGLE_QUOTE).append(SINGLE_QUOTE);
 704  0
                 break;
 705  
             case BACKSLASH:
 706  0
                 buf.append(escapeString);
 707  0
                 break;
 708  
             default:
 709  898748
                 buf.append(data[i]);
 710  
             }
 711  
         }
 712  230621
         buf.append(SINGLE_QUOTE);
 713  
 
 714  230621
         return buf.toString();
 715  
     }
 716  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.