KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > josql > expressions > BindVariable


1 /*
2  * Copyright 2004-2005 Gary Bentley
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may
5  * not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */

15 package org.josql.expressions;
16
17 import java.util.List JavaDoc;
18 import java.util.Map JavaDoc;
19 import java.util.HashMap JavaDoc;
20
21 import com.gentlyweb.utils.Getter;
22
23 import org.josql.Query;
24 import org.josql.QueryExecutionException;
25 import org.josql.QueryParseException;
26
27 import org.josql.internal.Utilities;
28
29 /**
30  * This class represents a "bind variable" used within a SQL statement.
31  * A bind variable can be either:
32  * <ul>
33  * <li>Named - a named bind variable is prefixed with ":", the value of the
34  * variable is set in the {@link Query} object via: {@link Query#setVariable(String,Object)}.
35  * The parser used (javacc generated) is a little picky about "reserved keywords" and as
36  * such you cannot use the following as the names of bind variables: select, from, limit,
37  * execute, on, all, results, where, having, order, by, group. If you <b>must</b> use one of
38  * reserved names then suffix it with "$", i.e. "execute$", "limit$" and so on.</li>
39  * <li>Anonymous - an anonymous bind variable is represented as "?". The value of the
40  * variable is set in the {@link Query} object via: {@link Query#setVariable(int,Object)}.
41  * Anonymous variables are assigned an internal integer index starting at 1 and they increase
42  * by 1.</li>
43  * <li>Special - there are 3 types of "special" bind variable:
44  * <ul>
45  * <li><b>:_currobj</b> - Indicates the current object within the execution scope.</li>
46  * <li><b>:_allobjs</b> - Indicates the current set of objects within the execution scope.</li>
47  * <li><b>:_query</b> - Indicates the Query object.</li>
48  * <li><b>:_grpby</b> - Indicates the Group By object. If there are more than 1 group by
49  * objects then :_grpby1 ... :_grpby2 ... :_grpbyX can be used to identify which
50  * group by object you mean. Remember that the group by object only has meaning
51  * if there is a group by statement.</li>
52  * <li><b>:_grpbys</b> - Indicates the set of group bys, this will be a list of lists, one
53  * list for each group by key, and each group by column will be in that list.
54  * <li><b>:_groupby</b> - A synonym for <b>:_grpby</b>.</li>
55  * <li><b>:_groupbys</b> - A synonym for <b>:_grpbys</b>.</li>
56  * </ul>
57  * <p>
58  * Special bind variables are mainly used in function calls however they can also be used
59  * just about anywhere.
60  * </li>
61  * </ul>
62  * <h3>Examples</h3>
63  * <pre>
64  * SELECT :_query,
65  * :_allobjs,
66  * :_currobj
67  * FROM java.lang.Object
68  *
69  * SELECT name
70  * FROM java.io.File
71  * WHERE length(:_currobj, name) > :length
72  * AND length > avg(:_query,:_allobjs,length)
73  * AND path LIKE '%' + ?
74  * </pre>
75  * <h3>Accessors</h3>
76  * <p>
77  * It is also possible for bind variables (including the special variables) to have accessors.
78  * For example:
79  * <pre>
80  * SELECT :_query.variables
81  * FROM java.lang.Object
82  * </pre>
83  * <p>
84  * Would cause all the bind variables in the query to be returned.
85  * Also, if the ? in the next query is an instance of <code>java.lang.String</code>.
86  * <pre>
87  * SELECT ?.length
88  * FROM java.lang.Object
89  * </pre>
90  * <p>
91  * Last Modified By: $Author: barrygently $<br />
92  * Last Modified On: $Date: 2004/12/20 16:22:43 $<br />
93  * Current Revision: $Revision: 1.2 $<br />
94  */

95 public class BindVariable extends ValueExpression
96 {
97
98     public static final String JavaDoc SPECIAL_NAME_PREFIX = "_";
99     private static Map JavaDoc SPECIAL_VAR_NAMES;
100
101     static
102     {
103
104     BindVariable.SPECIAL_VAR_NAMES = new HashMap JavaDoc ();
105     BindVariable.SPECIAL_VAR_NAMES.put (Query.QUERY_BIND_VAR_NAME,
106                         "");
107     BindVariable.SPECIAL_VAR_NAMES.put (Query.CURR_OBJ_VAR_NAME,
108                         "");
109     BindVariable.SPECIAL_VAR_NAMES.put (Query.ALL_OBJS_VAR_NAME,
110                         "");
111     BindVariable.SPECIAL_VAR_NAMES.put (Query.GRPBY_OBJ_VAR_NAME,
112                         "");
113     BindVariable.SPECIAL_VAR_NAMES.put (Query.GRPBY_OBJ_VAR_NAME_SYNONYM,
114                         "");
115     BindVariable.SPECIAL_VAR_NAMES.put (Query.PARENT_BIND_VAR_NAME,
116                         "");
117
118     }
119
120     private String JavaDoc name = null;
121     private Object JavaDoc val = null;
122     private boolean anon = false;
123     private String JavaDoc acc = null;
124     private Getter get = null;
125     private boolean groupByVar = false;
126     private int groupByInd = 0;
127
128     public boolean equals (Object JavaDoc o)
129     {
130
131     if (o == null)
132     {
133
134         return false;
135
136     }
137
138     if (!(o instanceof BindVariable))
139     {
140
141         return false;
142
143     }
144
145     BindVariable b = (BindVariable) o;
146
147     if ((b.getName () != null)
148         &&
149         (this.name != null)
150         &&
151         (b.getName ().equals (this.name))
152        )
153     {
154
155         if ((this.acc != null)
156         &&
157         (b.getAccessor () != null)
158         &&
159         (b.getAccessor ().equals (this.acc))
160            )
161         {
162
163         return true;
164
165         }
166
167     }
168
169     return false;
170
171     }
172
173     public String JavaDoc getAccessor ()
174     {
175
176     return this.acc;
177
178     }
179
180     public void setAccessor (String JavaDoc a)
181     {
182
183     this.acc = a;
184
185     }
186
187     /**
188      * Get the expected return type.
189      * The exact class returned here is dependent (obviously) on what the bind variable
190      * represents. Wherever possible it attempts to get the most specific class for the
191      * variable. It is generally better to set the variables prior to executing the:
192      * {@link Query#parse(String)} method to ensure that the correct class is returned here.
193      *
194      * @param q The Query object.
195      * @return The return type class or <code>java.lang.Object.class</code> if the class
196      * cannot be determined.
197      * @throws QueryParseException If the type cannot be determined.
198      */

199     public Class JavaDoc getExpectedReturnType (Query q)
200                                     throws QueryParseException
201     {
202
203     if (this.get != null)
204     {
205
206         return this.get.getType ();
207
208     }
209
210     if (this.val != null)
211     {
212
213         return this.val.getClass ();
214
215     }
216
217     return q.getVariableClass (this.name);
218
219     }
220
221     /**
222      * Initialises this bind variable.
223      * If the bind variable is "anonymous" then a name is gained for it from the
224      * Query object. If there is a value then it is gained from the Query object and
225      * cached. Also, if there is an accessor defined then it is inited where possible.
226      *
227      * @param q The Query object.
228      * @throws QueryParseException If the bind variable cannot be inited.
229      */

230     public void init (Query q)
231                   throws QueryParseException
232     {
233
234     if (this.anon)
235     {
236
237         this.name = q.getAnonymousBindVariableName ();
238
239     }
240
241     String JavaDoc n = this.name.toLowerCase ();
242
243     if ((n.startsWith (Query.GRPBY_OBJ_VAR_NAME))
244         ||
245         (n.startsWith (Query.GRPBY_OBJ_VAR_NAME_SYNONYM))
246        )
247     {
248
249         List JavaDoc grpBys = q.getGroupByColumns ();
250
251         // This is a group by... check for a group by clause.
252
if (grpBys == null)
253         {
254
255         throw new QueryParseException ("Use of special group by object bind variable: " +
256                            name +
257                            " is not valid when there are no GROUP BY clauses defined.");
258
259         }
260
261         String JavaDoc rest = "";
262
263         // Trim it down to see if there is a number.
264
if (n.startsWith (Query.GRPBY_OBJ_VAR_NAME))
265         {
266
267         rest = n.substring (Query.GRPBY_OBJ_VAR_NAME.length ());
268
269         }
270
271         if (n.startsWith (Query.GRPBY_OBJ_VAR_NAME_SYNONYM))
272         {
273         
274         rest = n.substring (Query.GRPBY_OBJ_VAR_NAME_SYNONYM.length ());
275
276         }
277
278         int grpbyind = 1;
279
280         if (rest.length () > 0)
281         {
282
283         // Should be a number.
284
try
285         {
286
287             grpbyind = Integer.parseInt (rest);
288
289         } catch (Exception JavaDoc e) {
290
291             throw new QueryParseException ("Special bind variable name: " +
292                            this.name +
293                            " is not valid, expected an integer number at end of name for indexing into GROUP BYs.");
294
295         }
296
297         if (grpbyind < 1)
298         {
299
300             throw new QueryParseException ("Special bind variable name: " +
301                            this.name +
302                            " is not valid, integer to index GROUP BYs must be a minimum of 1.");
303
304         }
305
306         if (grpbyind > (grpBys.size ()))
307         {
308
309             throw new QueryParseException ("Special bind variable name: " +
310                            this.name +
311                            " is not valid, integer references GROUP BY: " +
312                            grpbyind +
313                            " however there are only: " +
314                            grpBys.size () +
315                            " GROUP BYs defined.");
316
317         }
318
319         }
320
321         this.groupByVar = true;
322         this.groupByInd = grpbyind;
323
324     } else {
325
326         if (n.startsWith (BindVariable.SPECIAL_NAME_PREFIX))
327         {
328
329         // Make sure it's valid.
330
if (!BindVariable.SPECIAL_VAR_NAMES.containsKey (n))
331         {
332
333             throw new QueryParseException ("Bind variable name: " +
334                            name +
335                            " is not valid, bind variable names starting with: " +
336                            BindVariable.SPECIAL_NAME_PREFIX +
337                            " are reserved, and must be one of: " +
338                            BindVariable.SPECIAL_VAR_NAMES.keySet ());
339
340         }
341
342         }
343
344     }
345
346     // See if we already have this bind variable set...
347
this.val = q.getVariable (this.name);
348
349     // See if we have a "trailing" accessor.
350
if ((this.val != null)
351         &&
352         (this.acc != null)
353        )
354     {
355
356         this.initGetter (this.val);
357
358         try
359         {
360
361         this.val = this.get.getValue (this.val);
362
363         } catch (Exception JavaDoc e) {
364
365         throw new QueryParseException ("Unable to get value from accessor: " +
366                            this.acc +
367                            " and class: " +
368                            this.val.getClass ().getName () +
369                            " from bind variable: " +
370                            this.name,
371                            e);
372
373         }
374
375     }
376
377     // See if we can init the getter... there are times when it
378
// is possible even if the bind variable isn't available yet.
379
if ((this.acc != null)
380         &&
381         (this.get == null)
382        )
383     {
384
385         // Not over keen on this method but it will do for now...
386
// It precludes the init occurring if we are working on java.lang.Object
387
// objects... but how many times will that happen?
388
Class JavaDoc c = q.getVariableClass (this.name);
389
390         if (!c.isInstance (new Object JavaDoc ()))
391         {
392
393         // Init the getter.
394
this.initGetter (c);
395
396         }
397
398     }
399
400     }
401
402     public String JavaDoc getName ()
403     {
404
405     return this.name;
406
407     }
408
409     public boolean isAnonymous ()
410     {
411
412     return this.anon;
413
414     }
415
416     public void setAnonymous (boolean v)
417     {
418
419     this.anon = v;
420
421     }
422
423     public void setName (String JavaDoc name)
424     {
425
426     this.name = name;
427
428     }
429
430     private void initGetter (Object JavaDoc o)
431     {
432
433     // Get the class for the value.
434
Class JavaDoc c = o.getClass ();
435
436     this.initGetter (c);
437     
438     }
439
440     private void initGetter (Class JavaDoc c)
441     {
442
443     this.get = new Getter (this.acc,
444                    c);
445     
446     }
447
448     /**
449      * Gets the value of this bind variable.
450      *
451      * @param o The current object. Note that this variable isn't used in this method.
452      * @param q The Query object.
453      * @return The value.
454      * @throws QueryExecutionException If something goes wrong during the accessing
455      * of the value.
456      */

457     public Object JavaDoc getValue (Object JavaDoc o,
458                 Query q)
459                         throws QueryExecutionException
460     {
461
462     if (this.groupByVar)
463     {
464
465         o = q.getGroupByVariable (this.groupByInd);
466
467     } else {
468
469         o = q.getVariable (this.name);
470
471     }
472
473     if ((this.acc != null)
474         &&
475         (this.get == null)
476         &&
477         (o != null)
478        )
479     {
480
481         // Unable to get the accessor...
482
this.initGetter (o);
483
484     }
485
486     if (this.get != null)
487     {
488
489         try
490         {
491
492         o = this.get.getValue (o);
493
494         } catch (Exception JavaDoc e) {
495
496         throw new QueryExecutionException ("Unable to get value for accessor: " +
497                            this.acc +
498                            ", class: " +
499                            this.get.getBaseClass ().getName () +
500                            " from bind variable: " +
501                            this.name,
502                            e);
503
504         }
505
506     }
507
508     return o;
509
510     }
511
512     /**
513      * Returns whether the value of this bind variable represents a <code>true</code>
514      * value. See: {@link ArithmeticExpression#isTrue(Object,Query)} for details of how
515      * the return value is determined.
516      *
517      * @param o The current object. Not used in this method.
518      * @param q The Query object.
519      * @return <code>true</code> if the bind variable evaluates to <code>true</code>.
520      * @throws QueryExecutionException If a problem occurs during evaluation.
521      */

522     public boolean isTrue (Object JavaDoc o,
523                Query q)
524                        throws QueryExecutionException
525     {
526
527     o = this.getValue (o,
528                q);
529
530     if (o == null)
531     {
532         
533         return false;
534
535     }
536
537     if (Utilities.isNumber (o))
538     {
539
540         return Utilities.getDouble (o) > 0;
541
542     }
543
544     // Not null so return true...
545
return true;
546
547     }
548
549     /**
550      * Evaluates the value of this bind variable. This is just a thin-wrapper around:
551      * {@link #getValue(Object,Query)}.
552      *
553      * @param o The current object, not used in this method.
554      * @param q The Query object.
555      * @return The value of this bind variable.
556      * @throws QueryExecutionException If there is a problem getting the value.
557      */

558     public Object JavaDoc evaluate (Object JavaDoc o,
559                 Query q)
560                         throws QueryExecutionException
561     {
562
563     return this.getValue (o,
564                   q);
565
566     }
567
568     /**
569      * Returns a string version of this bind variable.
570      * Returns in the form: ? | ":" [ "_" ] Name
571      *
572      * @return A string version of the bind variable.
573      */

574     public String JavaDoc toString ()
575     {
576
577     StringBuffer JavaDoc buf = new StringBuffer JavaDoc ();
578
579     if (this.anon)
580     {
581
582         buf.append ("?");
583
584     } else {
585
586         buf.append (":");
587         buf.append (this.name);
588
589     }
590
591     if (this.acc != null)
592     {
593
594         buf.append (".");
595         buf.append (this.acc);
596
597     }
598
599     if (this.isBracketed ())
600     {
601
602         buf.insert (0,
603             "(");
604         buf.append (")");
605
606     }
607
608     return buf.toString ();
609
610     }
611
612     /**
613      * Will always return false since a bind variable cannot be fixed.
614      *
615      * @param q The Query object.
616      * @return <code>false</code> always.
617      */

618     public boolean hasFixedResult (Query q)
619     {
620
621     // A bind variable cannot have a fixed result.
622
return false;
623
624     }
625
626 }
627
Popular Tags