KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > iapi > types > Like


1 /*
2
3    Derby - Class org.apache.derby.iapi.types.Like
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    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, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.iapi.types;
23
24 // RESOLVE: MOVE THIS CLASS TO PROTOCOL (See LikeOperatorNode)
25

26 import org.apache.derby.iapi.services.sanity.SanityManager;
27
28 import org.apache.derby.iapi.error.StandardException;
29 import org.apache.derby.iapi.reference.SQLState;
30
31 import java.text.CollationElementIterator JavaDoc;
32 import java.text.Collator JavaDoc;
33 import java.text.RuleBasedCollator JavaDoc;
34 import java.util.Locale JavaDoc;
35
36 /**
37     Like matching algorithm. Not too speedy for %s.
38
39     SQL92 says the escape character can only and must be followed
40     by itself, %, or _. So if you choose % or _ as the escape character,
41     you can no longer do that sort of matching.
42
43     Not the most recent Like -- missing the unit tests
44
45     @author ames
46  */

47 public class Like {
48     private static final char anyChar = '_';
49     private static final char anyString = '%';
50
51     private static final String JavaDoc SUPER_STRING = "\uffff";
52
53     private Like() { // do not instantiate
54
}
55
56     /**
57         @param val value to compare. if null, result is null.
58         @param valLength length of val
59         @param pat pattern to compare. if null, result is null.
60         @param patLength length of pat
61         @param escape escape character. Must be 1 char long.
62             if null, no escape character is used.
63         @param escapeLength length of escape
64
65         @return null if val or pat null, otherwise true if match
66         and false if not.
67         @exception StandardException thrown if data invalid
68      */

69     public static Boolean JavaDoc like
70     (
71         char[] val,
72         int valLength,
73         char[] pat,
74         int patLength,
75         char[] escape,
76         int escapeLength
77     ) throws StandardException
78     {
79         return like(val, 0, valLength, pat, 0, patLength, escape, escapeLength);
80     }
81
82     /**
83         For national chars.
84         @param val value to compare. if null, result is null.
85         @param valLength length of val
86         @param pat pattern to compare. if null, result is null.
87         @param patLength length of pat
88         @param escape escape character. Must be 1 char long.
89             if null, no escape character is used.
90         @param escapeLength length of escape
91         @param collator The collator to use.
92
93         @return null if val or pat null, otherwise true if match
94         and false if not.
95         @exception StandardException thrown if data invalid
96      */

97     public static Boolean JavaDoc like
98     (
99         int[] val,
100         int valLength,
101         int[] pat,
102         int patLength,
103         int[] escape,
104         int escapeLength,
105         RuleBasedCollator JavaDoc collator
106     ) throws StandardException
107     {
108         return like(val, 0, valLength, pat, 0, patLength, escape, escapeLength, collator);
109     }
110
111     /* non-national chars */
112     private static Boolean JavaDoc like
113     (
114         char[] val,
115         int vLoc, // start at val[vLoc]
116
int vEnd, // end at val[vEnd]
117
char[] pat,
118         int pLoc, // start at pat[pLoc]
119
int pEnd, // end at pat[pEnd]
120
char[] escape,
121         int escapeLength
122     ) throws StandardException
123     {
124         char escChar = ' ';
125         boolean haveEsc = true;
126         
127         if (val == null) return null;
128         if (pat == null) return null;
129
130         if (escape == null)
131         {
132             haveEsc = false;
133         }
134         else
135         {
136             escChar = escape[0];
137         }
138
139         Boolean JavaDoc result;
140
141         while (true) {
142
143             if ((result = checkLengths(vLoc, vEnd, pLoc, pat, pEnd)) != null)
144             {
145                 return result;
146             }
147
148             // go until we get a special char in the pattern or hit EOS
149
while (pat[pLoc] != anyChar && pat[pLoc] != anyString &&
150                     ((! haveEsc) || pat[pLoc] != escChar)) {
151                 if (val[vLoc] == pat[pLoc])
152                 {
153                     vLoc++; pLoc++;
154     
155                     result = checkLengths(vLoc, vEnd, pLoc, pat, pEnd);
156                     if (result != null)
157                         return result;
158                 }
159                 else
160                 {
161                     return Boolean.FALSE;
162                 }
163             }
164
165             // deal with escChar first, as it can be escaping a special char
166
// and can be a special char itself.
167
if (haveEsc && pat[pLoc] == escChar) {
168                 pLoc++;
169                 if (pLoc == pEnd) {
170                     throw StandardException.newException(SQLState.LANG_INVALID_ESCAPE_SEQUENCE);
171                 }
172                 if (pat[pLoc] != escChar &&
173                     pat[pLoc] != anyChar &&
174                     pat[pLoc] != anyString) {
175                     throw StandardException.newException(SQLState.LANG_INVALID_ESCAPE_SEQUENCE);
176                 }
177                 // regardless of the char in pat, it must match exactly:
178
if (val[vLoc] == pat[pLoc]) {
179                     vLoc++; pLoc++;
180     
181                     result = checkLengths(vLoc, vEnd, pLoc, pat, pEnd);
182                     if (result != null)
183                         return result;
184                 }
185                 else return Boolean.FALSE;
186             }
187             else if (pat[pLoc] == anyChar) {
188                 // regardless of the char, it matches
189
vLoc++; pLoc++;
190     
191                 result = checkLengths(vLoc, vEnd, pLoc, pat, pEnd);
192                 if (result != null)
193                     return result;
194             }
195             else if (pat[pLoc] == anyString) {
196                 // catch the simple cases -- end of the pattern or of the string
197
if (pLoc+1 == pEnd)
198                     return Boolean.TRUE;
199
200                 // would return true, but caught in checkLengths above
201
if (SanityManager.DEBUG)
202                     SanityManager.ASSERT(vLoc!=vEnd,
203                         "Should have been found already");
204
205                 //if (vLoc == vEnd) // caught in checkLengths
206
//return Boolean.TRUE;
207
// check if remainder of pattern is anyString's
208
// if escChar == anyString, we couldn't be here
209
boolean anys = true;
210                 for (int i=pLoc+1;i<pEnd;i++)
211                     if (pat[i]!=anyString) {
212                         anys=false;
213                         break;
214                     }
215                 if (anys) return Boolean.TRUE;
216
217                 // pattern can match 0 or more chars in value.
218
// to test that, we take the remainder of pattern and
219
// apply it to ever-shorter remainders of value until
220
// we hit a match.
221

222                 // the loop never continues from this point -- we will
223
// always generate an answer here.
224

225                 // REMIND: there are smarter ways to pick the remainders
226
// and do this matching.
227

228                 // num chars left in value includes current char
229
int vRem = vEnd - vLoc;
230
231                 int n=0;
232
233                 // num chars left in pattern excludes the anychar
234
int minLen = getMinLen(pat, pLoc+1, pEnd, haveEsc, escChar);
235                 for (int i=vRem; i>=minLen; i--)
236                 {
237                     Boolean JavaDoc restResult = Like.like(val,vLoc+n,vLoc+n+i,pat,pLoc+1,pEnd,escape,escapeLength);
238                     if (SanityManager.DEBUG)
239                     {
240                         if (restResult == null)
241                         {
242                             String JavaDoc vStr = new String JavaDoc(val,vLoc+n,i);
243                             String JavaDoc pStr = new String JavaDoc(pat,pLoc+1,pEnd-(pLoc+1));
244                             SanityManager.THROWASSERT("null result on like(value = "+vStr+", pat = "+pStr+")");
245                         }
246                     }
247                     if (restResult.booleanValue())
248                         return restResult;
249
250                     n++;
251                 }
252                 // none of the possibilities worked
253
return Boolean.FALSE;
254             }
255         }
256     }
257
258     /* national chars */
259     private static Boolean JavaDoc like
260     (
261         int[] val,
262         int vLoc, // start at val[vLoc]
263
int vEnd, // end at val[vEnd]
264
int[] pat,
265         int pLoc, // start at pat[pLoc]
266
int pEnd, // end at pat[pEnd]
267
int[] escape,
268         int escapeLength,
269         RuleBasedCollator JavaDoc collator
270     ) throws StandardException
271     {
272         int[] escCharInts = null;
273         boolean haveEsc = true;
274         int[] anyCharInts = new int[1]; // assume only 1 int
275
int[] anyStringInts = new int[1]; // assume only 1 int
276

277         if (val == null) return null;
278         if (pat == null) return null;
279
280         if (escape == null)
281         {
282             haveEsc = false;
283         }
284         else
285         {
286             escCharInts = escape;
287         }
288
289         Boolean JavaDoc result;
290
291         // get the collation integer representing "_"
292
CollationElementIterator JavaDoc cei =
293                                     collator.getCollationElementIterator("_");
294         anyCharInts[0] = cei.next();
295         {
296             int nextInt;
297
298             // There may be multiple ints representing this character
299
while ((nextInt = cei.next()) != CollationElementIterator.NULLORDER)
300             {
301                 int[] temp = new int[anyCharInts.length + 1];
302                 for (int index = 0; index < anyCharInts.length; index++)
303                 {
304                     temp[index] = anyCharInts[index];
305                 }
306                 temp[anyCharInts.length] = nextInt;
307                 anyCharInts = temp;
308             }
309         }
310         // get the collation integer representing "%"
311
cei = collator.getCollationElementIterator("%");
312         anyStringInts[0] = cei.next();
313         {
314             int nextInt;
315
316             // There may be multiple ints representing this character
317
while ((nextInt = cei.next()) != CollationElementIterator.NULLORDER)
318             {
319                 int[] temp = new int[anyStringInts.length + 1];
320                 for (int index = 0; index < anyStringInts.length; index++)
321                 {
322                     temp[index] = anyStringInts[index];
323                 }
324                 temp[anyStringInts.length] = nextInt;
325                 anyStringInts = temp;
326             }
327         }
328
329         while (true)
330         {
331             // returns null if more work to do, otherwise match Boolean
332
result = checkLengths(vLoc, vEnd, pLoc, pat, pEnd, anyStringInts);
333             if (result != null)
334                 return result;
335
336             // go until we get a special char in the pattern or hit EOS
337
while ( (! matchSpecial(pat, pLoc, pEnd, anyCharInts)) &&
338                     (! matchSpecial(pat, pLoc, pEnd, anyStringInts)) &&
339                     ((! haveEsc)
340                         || (! matchSpecial(pat, pLoc, pEnd, escCharInts))))
341             {
342                 if (val[vLoc] == pat[pLoc])
343                 {
344                     vLoc++; pLoc++;
345     
346                     result = checkLengths(vLoc, vEnd, pLoc,
347                                 pat, pEnd, anyStringInts);
348                     if (result != null)
349                     {
350                         return result;
351                     }
352                 }
353                 else
354                 {
355                     return Boolean.FALSE;
356                 }
357             }
358
359             // deal with escCharInt first, as it can be escaping a special char
360
// and can be a special char itself.
361
if (haveEsc && matchSpecial(pat, pLoc, pEnd, escCharInts))
362             {
363                 pLoc += escCharInts.length;
364                 if (pLoc == pEnd)
365                 {
366                     throw StandardException.newException(
367                         SQLState.LANG_INVALID_ESCAPE_SEQUENCE);
368                 }
369
370                 int[] specialInts = null;
371                 if (matchSpecial(pat, pLoc, pEnd, escCharInts))
372                 {
373                     specialInts = escCharInts;
374                 }
375                 if (matchSpecial(pat, pLoc, pEnd, anyCharInts))
376                 {
377                     specialInts = anyCharInts;
378                 }
379                 if (matchSpecial(pat, pLoc, pEnd, anyStringInts))
380                 {
381                     specialInts = anyStringInts;
382                 }
383                 if (specialInts == null)
384                 {
385                     throw StandardException.newException(SQLState.LANG_INVALID_ESCAPE_SEQUENCE);
386                 }
387                 // regardless of the char in pat, it must match exactly:
388
for (int index = 0; index < specialInts.length; index++)
389                 {
390                     if (val[vLoc + index] != pat[pLoc + index])
391                     {
392                         return Boolean.FALSE;
393                     }
394                 }
395
396                 vLoc += specialInts.length;
397                 pLoc += specialInts.length;
398     
399                 // returns null if more work to do, otherwise match Boolean
400
result = checkLengths(vLoc, vEnd,
401                         pLoc, pat, pEnd, anyStringInts);
402
403                 if (result != null)
404                     return result;
405             }
406             else if (matchSpecial(pat, pLoc, pEnd, anyCharInts))
407             {
408                 // regardless of the char, it matches
409
vLoc += anyCharInts.length;
410                 pLoc += anyCharInts.length;
411     
412                 result = checkLengths(vLoc, vEnd, pLoc, pat, pEnd, anyStringInts);
413                 if (result != null)
414                     return result;
415             }
416             else if (matchSpecial(pat, pLoc, pEnd, anyStringInts))
417             {
418                 // catch the simple cases -- end of the pattern or of the string
419
if (pLoc+1 == pEnd)
420                     return Boolean.TRUE;
421
422                 // would return true, but caught in checkLengths above
423
if (SanityManager.DEBUG)
424                     SanityManager.ASSERT(vLoc!=vEnd,
425                         "Should have been found already");
426
427                 if (vLoc == vEnd)
428                     return Boolean.TRUE;
429
430                 // check if remainder of pattern is anyString's
431
// if escChar == anyString, we couldn't be here
432
// If there is an escape in the pattern we break
433
boolean allPercentChars = true;
434                 for (int i=pLoc+1;i<pEnd;i++)
435                 {
436                     if (! matchSpecial(pat, i, pEnd, anyStringInts))
437                     {
438                         allPercentChars=false;
439                         break;
440                     }
441                 }
442                 if (allPercentChars)
443                     return Boolean.TRUE;
444
445                 // pattern can match 0 or more chars in value.
446
// to test that, we take the remainder of pattern and
447
// apply it to ever-shorter remainders of value until
448
// we hit a match.
449

450                 // the loop never continues from this point -- we will
451
// always generate an answer here.
452

453                 // REMIND: there are smarter ways to pick the remainders
454
// and do this matching.
455

456                 // num chars left in value includes current char
457
int vRem = vEnd - vLoc;
458
459                 int n=0;
460
461                 // num chars left in pattern excludes the anyString
462
int minLen = getMinLen(pat, pLoc+1, pEnd, haveEsc, escCharInts, anyStringInts);
463                 for (int i=vRem; i>=minLen; i--)
464                 {
465                     Boolean JavaDoc restResult = Like.like(val,vLoc+n,vLoc+n+i,pat,pLoc+1,pEnd,escape,escapeLength, collator);
466                     if (SanityManager.DEBUG)
467                     {
468                         if (restResult == null)
469                         {
470                             SanityManager.THROWASSERT("null result on like(vLoc+n = "+(vLoc+n)+", i = "+i+
471                                                       ", pLoc+1 = " + (pLoc+1) + ", pEnd-(pLoc+1) = " +
472                                                       (pEnd-(pLoc+1)) + ")");
473                         }
474                     }
475                     if (restResult.booleanValue())
476                         return restResult;
477
478                     n++;
479                 }
480                 // none of the possibilities worked
481
return Boolean.FALSE;
482             }
483         }
484     }
485
486     /**
487         Calculate the shortest length string that could match this pattern for non-national chars
488      */

489     static int getMinLen(char[] pattern, int pStart, int pEnd, boolean haveEsc, char escChar)
490     {
491         int m=0;
492         for (int l = pStart; l<pEnd; )
493         {
494             if (haveEsc && pattern[l] == escChar) { // need one char
495
l+=2;
496                 m++;
497             }
498             else if (pattern[l] == anyString) {
499                 l++; // anyString, nothing needed
500
}
501             else { // anyChar or other chars, need one char
502
l++; m++;
503             }
504         }
505         return m;
506     }
507
508     /**
509         Calculate the shortest length string that could match this pattern for national chars
510      */

511     static int getMinLen(int[] pattern, int pStart, int pEnd, boolean haveEsc,
512                          int[] escCharInts, int[] anyStringInts)
513     {
514         int m=0;
515         for (int l = pStart; l<pEnd; )
516         {
517             if (haveEsc && matchSpecial(pattern, l, pEnd, escCharInts))
518             {
519                 l += escCharInts.length + 1;
520                 m += escCharInts.length;
521             }
522             else if (matchSpecial(pattern, l, pEnd, anyStringInts))
523             {
524                 l += anyStringInts.length; // anyString, nothing needed
525
}
526             else
527             { // anyChar or other chars, need one char
528
l++; m++;
529             }
530         }
531         return m;
532     }
533
534     /**
535      * checkLengths -- non-national chars
536      *
537      * Returns null if we are not done.
538      * Returns true if we are at the end of our value and pattern
539      * Returns false if there is more pattern left but out of input value
540      *
541      * @param vLoc current index into char[] val
542      * @param vEnd end index or our value
543      * @param pLoc current index into our char[] pattern
544      * @param pat pattern char []
545      * @param pEnd end index of our pattern []
546      */

547
548     static Boolean JavaDoc checkLengths(int vLoc, int vEnd,
549             int pLoc, char[] pat, int pEnd)
550     {
551         if (vLoc == vEnd)
552         {
553             if (pLoc == pEnd)
554             {
555                 return Boolean.TRUE;
556             }
557             else
558             {
559                 // if remainder of pattern is anyString chars, ok
560
for (int i=pLoc; i<pEnd; i++)
561                 {
562                     if (pat[i] != anyString)
563                     {
564                         return Boolean.FALSE; // more to match
565
}
566                 }
567                 return Boolean.TRUE;
568             }
569         }
570         else if (pLoc == pEnd)
571         {
572             return Boolean.FALSE; // ran out of pattern
573
}
574         else return null; // still have strings to match, not done
575
}
576
577     /**
578      * checkLengths -- national chars
579      *
580      * Returns null if we are not done.
581      * Returns true if we are at the end of our value and pattern
582      * Returns false if there is more pattern left but out of input value
583      *
584      * @param vLoc current index into int[] val
585      * @param vEnd end index or our value
586      * @param pLoc current index into our int[] pattern
587      * @param pat pattern int []
588      * @param pEnd end index of our pattern []
589      */

590
591     static Boolean JavaDoc checkLengths(int vLoc, int vEnd,
592             int pLoc, int[] pat, int pEnd, int[] anyStringInts)
593     {
594         if (vLoc == vEnd)
595         {
596             if (pLoc == pEnd)
597             {
598                 return Boolean.TRUE;
599             }
600             else
601             {
602                 // if remainder of pattern is anyString chars, ok
603
for (int i=pLoc; i<pEnd; i += anyStringInts.length)
604                 {
605                     if (! matchSpecial(pat, i, pEnd, anyStringInts))
606                     {
607                         return Boolean.FALSE;
608                     }
609                 }
610                 return Boolean.TRUE;
611             }
612         }
613         else if (pLoc == pEnd)
614         {
615             return Boolean.FALSE; // ran out of pattern
616
}
617         else return null; // still have strings to match, not done
618
}
619
620     /**
621      * matchSpecial
622      *
623      * check the pattern against the various special character arrays.
624      * The array can be anyStringInts, anyCharInts or anyEscChars (always 1)
625      */

626
627     private static boolean matchSpecial(int[] pat, int patStart, int patEnd, int[] specialInts)
628     {
629         //
630
// multi-collation units per char can exceed the pattern length
631
// and we fall around the 2nd if statement and falsely return true.
632
//
633
if (specialInts.length > patEnd - patStart)
634             return false;
635         if (specialInts.length <= patEnd - patStart)
636         {
637             for (int index = 0; index < specialInts.length; index++)
638             {
639                 if (pat[patStart + index] != specialInts[index])
640                 {
641                     return false; // more to match
642
}
643             }
644         }
645         return true;
646     }
647
648
649     /*
650         Most typical interface for non-national chars
651      */

652     public static Boolean JavaDoc like(char[] value, int valueLength, char[] pattern, int patternLength) throws StandardException {
653         if (value == null || pattern == null) return null;
654         return like(value, valueLength, pattern, patternLength, null, 0);
655     }
656
657     /*
658         Most typical interface for national chars
659      */

660     public static Boolean JavaDoc like(int[] value, int valueLength, int[] pattern, int patternLength, RuleBasedCollator JavaDoc collator)
661         throws StandardException
662     {
663         if (value == null || pattern == null) return null;
664         return like(value, valueLength, pattern, patternLength, null, 0, collator);
665     }
666
667     // Methods for LIKE transformation at preprocess time:
668

669     /**
670      * Determine whether or not this LIKE can be transformed into optimizable
671      * clauses. It can if the pattern is non-null and if the length == 0 or
672      * the first character is not a wild card.
673      *
674      * @param pattern The right side of the LIKE
675      *
676      * @return Whether or not the LIKE can be transformed
677      */

678
679     public static boolean isOptimizable(String JavaDoc pattern)
680     {
681         if (pattern == null)
682         {
683             return false;
684         }
685
686         if (pattern.length() == 0) {
687             return true;
688         }
689
690         // if we have pattern matching at start of string, no optimization
691
char firstChar = pattern.charAt(0);
692
693         return (firstChar != anyChar && firstChar != anyString);
694     }
695
696     public static String JavaDoc greaterEqualStringFromParameter(String JavaDoc pattern, int maxWidth)
697         throws StandardException {
698
699         if (pattern == null)
700             return null;
701
702         return greaterEqualString(pattern, (String JavaDoc) null, maxWidth);
703     }
704
705     public static String JavaDoc greaterEqualStringFromParameterWithEsc(String JavaDoc pattern, String JavaDoc escape, int maxWidth)
706         throws StandardException {
707
708         if (pattern == null)
709             return null;
710
711         return greaterEqualString(pattern, escape, maxWidth);
712     }
713
714     /**
715      * Return the substring from the pattern for the optimization >= clause.
716      *
717      * @param pattern The right side of the LIKE
718      * @param escape The escape clause
719      * @param maxWidth Maximum length of column, for null padding
720      *
721      * @return The String for the >= clause
722      */

723     public static String JavaDoc greaterEqualString(String JavaDoc pattern, String JavaDoc escape, int maxWidth)
724         throws StandardException
725     {
726
727         int firstAnyChar = pattern.indexOf(anyChar);
728         int firstAnyString = pattern.indexOf(anyString);
729
730         //
731
// For Escape we don't utilize any of the stylish code
732
// below but brute force walk the pattern to find out
733
// what is there, while stripping escapes
734
//
735

736         if ((escape != null) && (escape.length() != 0))
737         {
738             char escChar = escape.charAt(0);
739             if (pattern.indexOf(escChar) != -1)
740             {
741                 // we return a string stripping out the escape char
742
// leaving the _? in place as normal chars.
743

744                 return padWithNulls(greaterEqualString(pattern, escChar), maxWidth);
745             }
746             // drop through if no escape found
747
}
748
749         if (firstAnyChar == -1)
750         {
751             if (firstAnyString != -1) // no _, found %
752
{
753                 pattern = pattern.substring(0, firstAnyString);
754             }
755         }
756         else if (firstAnyString == -1)
757         {
758             pattern = pattern.substring(0, firstAnyChar);
759         }
760         else
761         {
762             pattern = pattern.substring(0, (firstAnyChar > firstAnyString) ?
763                                             firstAnyString :
764                                             firstAnyChar);
765         }
766         return padWithNulls(pattern, maxWidth);
767     }
768
769     /**
770      * greaterEqualString -- for Escape clause only
771      *
772      * Walk the pattern character by character
773      * @param pattern like pattern to build from
774      * @param escChar the escape character in the pattern
775      */

776
777     private static String JavaDoc greaterEqualString(String JavaDoc pattern, char escChar)
778         throws StandardException
779     {
780         int patternLen = pattern.length();
781         char[] patternChars = new char[patternLen];
782         char[] result = new char[patternLen];
783         pattern.getChars(0, patternLen, patternChars, 0);
784
785         int r = 0;
786         for (int p = 0; p < patternLen && r < patternLen; p++)
787         {
788             char c = patternChars[p];
789             if (c == escChar)
790             {
791                 p++; // don't copy the escape char
792

793                 // run out?
794
if (p >= patternLen)
795                     throw StandardException.newException(
796                             SQLState.LANG_INVALID_ESCAPE_SEQUENCE);
797                 result[r++] = patternChars[p];
798                 continue;
799             }
800
801             // stop on first pattern matching char
802
if (c == anyChar || c == anyString)
803             {
804                 return new String JavaDoc(result, 0, r);
805             }
806
807             result[r++] = patternChars[p];
808         }
809
810         // no pattern chars
811
return new String JavaDoc(result, 0, r);
812     }
813
814     /**
815      * stripEscapesNoPatternChars
816      *
817      * @param pattern pattern String to search
818      * @param escChar the escape character
819      *
820      * @return a stripped of ESC char string if no pattern chars, null otherwise
821      * @exception StandardException thrown if data invalid
822      */

823
824     public static String JavaDoc
825         stripEscapesNoPatternChars(String JavaDoc pattern, char escChar)
826         throws StandardException
827     {
828         int patternLen = pattern.length();
829         char[] patternChars = new char[patternLen];
830         char[] result = new char[patternLen];
831         pattern.getChars(0, patternLen, patternChars, 0);
832
833         int r = 0;
834         for (int p = 0; p < patternLen && r < patternLen; p++)
835         {
836             char c = pattern.charAt(p);
837             if (c == escChar)
838             {
839                 p++; // don't copy the escape char
840

841                 // run out?
842
if (p >= patternLen)
843                     throw StandardException.newException(
844                             SQLState.LANG_INVALID_ESCAPE_SEQUENCE);
845                 result[r++] = patternChars[p];
846                 continue;
847             }
848
849             // die on first pattern matching char
850
if (c == anyChar || c == anyString)
851             {
852                 return null;
853             }
854
855             result[r++] = patternChars[p];
856         }
857         return new String JavaDoc(result, 0, r);
858     }
859
860     public static String JavaDoc lessThanStringFromParameter(String JavaDoc pattern, int maxWidth)
861         throws StandardException
862     {
863         if (pattern == null)
864             return null;
865         return lessThanString(pattern, null, maxWidth);
866     }
867
868     public static String JavaDoc lessThanStringFromParameterWithEsc(String JavaDoc pattern, String JavaDoc escape, int maxWidth)
869         throws StandardException
870     {
871         if (pattern == null)
872             return null;
873         return lessThanString(pattern, escape, maxWidth);
874     }
875
876     /**
877      * Return the substring from the pattern for the < clause.
878      *
879      * @param pattern The right side of the LIKE
880      * @param escape The escape clause
881      * @param maxWidth Maximum length of column, for null padding
882      *
883      * @return The String for the < clause
884      * @exception StandardException thrown if data invalid
885      */

886     public static String JavaDoc lessThanString(String JavaDoc pattern, String JavaDoc escape, int maxWidth)
887         throws StandardException
888     {
889         int lastUsableChar;
890         char oldLastChar;
891         char newLastChar;
892         final int escChar;
893
894         if ((escape != null) && (escape.length() !=0))
895         {
896             escChar = escape.charAt(0);
897         }
898         else {
899             // Set escape character to a value outside the char range,
900
// so that comparison with a char always evaluates to false.
901
escChar = -1;
902         }
903
904         /* Find the last non-wildcard character in the pattern
905          * and increment it. In the most common case,
906          * "asdf%" becomes "asdg". However, we need to
907          * handle the following:
908          *
909          * pattern return
910          * ------- ------
911          * "" SUPER_STRING (match against super string)
912          * "%..." SUPER_STRING (match against super string)
913          * "_..." SUPER_STRING (match against super string)
914          * "asdf%" "asdg"
915          */

916
917         StringBuffer JavaDoc upperLimit = new StringBuffer JavaDoc(maxWidth);
918
919         // Extract the string leading up to the first wildcard.
920
for (int i = 0; i < pattern.length(); i++) {
921             char c = pattern.charAt(i);
922             if (c == escChar) {
923                 if (++i >= pattern.length()) {
924                     throw StandardException.newException(
925                             SQLState.LANG_INVALID_ESCAPE_SEQUENCE);
926                 }
927                 c = pattern.charAt(i);
928             } else if (c == anyChar || c == anyString) {
929                 break;
930             }
931             upperLimit.append(c);
932         }
933
934         // Pattern is empty or starts with wildcard.
935
if (upperLimit.length() == 0) {
936             return SUPER_STRING;
937         }
938
939         // Increment the last non-wildcard character.
940
lastUsableChar = upperLimit.length() - 1;
941         oldLastChar = upperLimit.charAt(lastUsableChar);
942         newLastChar = oldLastChar;
943         newLastChar++;
944
945         // Check for degenerate roll over
946
if (newLastChar < oldLastChar)
947         {
948             return SUPER_STRING;
949         }
950
951         upperLimit.setCharAt(lastUsableChar, newLastChar);
952
953         // Pad the string with nulls.
954
if (upperLimit.length() < maxWidth) {
955             upperLimit.setLength(maxWidth);
956         }
957
958         return upperLimit.toString();
959     }
960     
961     /**
962      * Return whether or not the like comparison is still needed after
963      * performing the like transformation on a constant string. The
964      * comparison is not needed if the constant string is of the form:
965      * CONSTANT% (constant followed by a trailing %)
966      *
967      * @param pattern The right side of the LIKE
968      *
969      * @return Whether or not the like comparison is still needed.
970      */

971     public static boolean isLikeComparisonNeeded(String JavaDoc pattern)
972     {
973         int firstAnyChar = pattern.indexOf(anyChar);
974         int firstAnyString = pattern.indexOf(anyString);
975
976         if (SanityManager.DEBUG)
977         {
978             SanityManager.ASSERT(pattern.length() != 0,
979                 "pattern expected to be non-zero length");
980         }
981
982         // if no pattern matching characters, no LIKE needed
983
if (firstAnyChar == -1 && firstAnyString == -1)
984             return false;
985
986         /* Needed if string containts anyChar */
987         if (firstAnyChar != -1)
988         {
989             return true;
990         }
991
992         /* Needed if string contains and anyString in any place
993          * other than the last character.
994          */

995         if (firstAnyString != pattern.length() - 1)
996         {
997             return true;
998         }
999
1000        return false;
1001    }
1002
1003    /**
1004     * Pad a string with null characters, in order to make it &gt; and &lt;
1005     * comparable with SQLChar.
1006     *
1007     * @param string The string to pad
1008     * @param len Max number of characters to pad to
1009     * @return the string padded with 0s up to the given length
1010     */

1011    private static String JavaDoc padWithNulls(String JavaDoc string, int len)
1012    {
1013        if(string.length() >= len)
1014            return string;
1015
1016        StringBuffer JavaDoc buf = new StringBuffer JavaDoc(len).append(string);
1017        buf.setLength(len);
1018        
1019        return buf.toString();
1020    }
1021}
1022
Popular Tags