KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > josql > functions > CollectionFunctions


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.functions;
16
17 import java.util.List JavaDoc;
18 import java.util.ArrayList JavaDoc;
19 import java.util.Map JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.TreeMap JavaDoc;
22 import java.util.LinkedHashMap JavaDoc;
23 import java.util.Collections JavaDoc;
24
25 import com.gentlyweb.utils.Getter;
26 import com.gentlyweb.utils.GeneralComparator;
27
28 import org.josql.Query;
29 import org.josql.QueryExecutionException;
30
31 import org.josql.expressions.Expression;
32
33 import org.josql.internal.Utilities;
34
35 /**
36  * Defines a set of functions that operate on "collections" of objects in some way.
37  */

38 public class CollectionFunctions extends AbstractFunctionHandler
39 {
40
41     private Map JavaDoc foreachQueryCache = null;
42
43     /**
44      * The id that can be used to get the "CollectionFunctions" handler object from
45      * the Query object.
46      */

47     public static final String JavaDoc HANDLER_ID = "_internal_collection";
48
49     /**
50      * Sort a list according to it's "natural" ordering (see {@link Collections#sort(List)}).
51      *
52      * @param objs The list of objects to sort.
53      * @return The sorted list, according to their natural ordering.
54      */

55     public List JavaDoc sort (List JavaDoc objs)
56     {
57
58     Collections.sort (objs);
59
60     return objs;
61
62     }
63
64     /**
65      * Sort a Map by the keys in ascending order (for more optionality in the sort and ordering
66      * see: {@link #sort(Map,String,String)}).
67      *
68      * @param m The map to sort.
69      * @return A List sorted according to the key in ascending order.
70      */

71     public List JavaDoc sort (Map JavaDoc m)
72     {
73
74     return this.sort (m,
75               "key",
76               GeneralComparator.ASC);
77
78     }
79
80     /**
81      * Sort a Map by it's keys or values in ascending order (for more optionality in the sort and ordering
82      * see: {@link #sort(Map,String,String)}).
83      *
84      * @param m The map to sort.
85      * @param type Should be either: "key" or "value" to indicate which item to sort on.
86      * Use <code>null</code> for key.
87      * @return A List sorted according to the key in ascending order.
88      */

89     public List JavaDoc sort (Map JavaDoc m,
90               String JavaDoc type)
91     {
92
93     return this.sort (m,
94               type,
95               GeneralComparator.ASC);
96
97     }
98
99     /**
100      * Sort a Map by either it's key or value.
101      *
102      * @param m The map to sort.
103      * @param type Should be either: "key" or "value" to indicate which item to sort on.
104      * Use <code>null</code> for key.
105      * @param dir The direction you want to sort on, either "asc" or "desc". Use <code>null</code>
106      * for "asc".
107      * @return A List sorted according to the key or value.
108      */

109     public List JavaDoc sort (Map JavaDoc m,
110               String JavaDoc type,
111               String JavaDoc dir)
112     {
113
114     boolean key = true;
115
116     String JavaDoc acc = "key";
117
118     if ((type != null)
119         &&
120         (type.equalsIgnoreCase ("value"))
121        )
122     {
123
124         acc = "value";
125
126     }
127
128     String JavaDoc d = GeneralComparator.ASC;
129
130     if (dir != null)
131     {
132
133         dir = dir.toUpperCase ();
134
135         if (dir.equals (GeneralComparator.DESC))
136         {
137
138         d = GeneralComparator.DESC;
139
140         }
141
142     }
143
144     GeneralComparator gc = new GeneralComparator (Map.Entry JavaDoc.class);
145
146     gc.addField (acc,
147              d);
148
149     List JavaDoc l = new ArrayList JavaDoc (m.entrySet ());
150
151     Collections.sort (l,
152               gc);
153
154     return l;
155
156     }
157
158     /**
159      * Get a value from the specified Map.
160      *
161      * @param m The map of objects.
162      * @param exp The expression is evaluated (in the context of the current object) and the
163      * value returned used as the key to the Map, the value it maps to
164      * (which may be null) is returned.
165      * @return The value that the <b>exp</b> value maps to, may be null.
166      */

167     public Object JavaDoc get (Map JavaDoc m,
168                Expression exp)
169                    throws QueryExecutionException
170     {
171
172     // Evaluate the expression.
173
// Get the current object.
174
return m.get (exp.getValue (this.q.getCurrentObject (),
175                     this.q));
176
177     }
178
179     /**
180      * Get a value from the specified List.
181      *
182      * @param l The list of objects.
183      * @param n The index, indices start at 0.
184      * @return The value of the <b>i</b>th element from the list of objects. Return <code>null</code>
185      * if <b>n</b> is out of range.
186      */

187     public Object JavaDoc get (List JavaDoc l,
188                Number JavaDoc n)
189     {
190
191     int i = n.intValue ();
192
193     if ((i > l.size ())
194         ||
195         (i < 0)
196        )
197     {
198
199         return null;
200
201     }
202
203     return l.get (i);
204
205     }
206
207     /**
208      * For each of the objects in the <b>objs</b> List get the value from each one
209      * using the <b>accessor</b> and compare it to the <b>value</b> parameter. The value
210      * param is converted to a string and then to a Boolean value using: {@link Boolean#valueOf(String)}.
211      *
212      * @param objs The list of objects to iterate over.
213      * @param exp The expression to use to get the value from the object in the List.
214      * @param value The value to compare the result of the accessor against. If the parm is <code>null</code>
215      * then it defaults to {@link Boolean#FALSE}.
216      * @return A count of how many times the accessor evaluated to the same value of the
217      * <b>value</b> parm.
218      * @throws QueryExecutionException If the value from the accessor cannot be gained or if
219      * the compare cannot be performed.
220      */

221     public int count (List JavaDoc objs,
222               Expression exp,
223               Object JavaDoc value)
224                   throws QueryExecutionException
225     {
226
227     Boolean JavaDoc b = Boolean.FALSE;
228
229     if (value != null)
230     {
231
232         b = Boolean.valueOf (value.toString ());
233
234     }
235
236     int count = 0;
237
238     List JavaDoc retVals = new ArrayList JavaDoc ();
239
240     int size = objs.size ();
241
242     for (int i = 0; i < size; i++)
243     {
244
245         Object JavaDoc o = objs.get (i);
246
247         Object JavaDoc v = null;
248
249         try
250         {
251
252         if (Utilities.compare (exp.getValue (o,
253                              this.q),
254                        b) == 0)
255         {
256
257             count++;
258
259         }
260
261         } catch (Exception JavaDoc e) {
262
263         throw new QueryExecutionException ("Unable to get value from expression: " +
264                            exp +
265                            " for item: " +
266                            i +
267                            " from the list of objects.",
268                            e);
269
270         }
271
272     }
273
274     return count;
275
276     }
277
278     public int count (Expression exp)
279                   throws QueryExecutionException
280     {
281
282     return this.count ((List JavaDoc) this.q.getVariable (Query.ALL_OBJS_VAR_NAME),
283                exp);
284
285     }
286
287     public int count (List JavaDoc allobjs,
288               Expression exp)
289                   throws QueryExecutionException
290     {
291
292     int count = 0;
293
294     List JavaDoc retVals = new ArrayList JavaDoc ();
295
296     int size = allobjs.size ();
297
298     for (int i = 0; i < size; i++)
299     {
300
301         Object JavaDoc o = allobjs.get (i);
302
303         Object JavaDoc v = null;
304
305         try
306         {
307
308         if (exp.isTrue (o,
309                 this.q))
310         {
311
312             count++;
313
314         }
315
316         } catch (Exception JavaDoc e) {
317
318         throw new QueryExecutionException ("Unable to determine whether expression: \"" +
319                            exp +
320                            "\" is true for object at index: " +
321                            i +
322                            " from the list of objects.",
323                            e);
324
325         }
326
327     }
328
329     return count;
330
331     }
332
333     public List JavaDoc toList (List JavaDoc allobjs,
334             Expression exp,
335             String JavaDoc saveValueName)
336                         throws QueryExecutionException
337     {
338
339     return this.collect (allobjs,
340                  exp,
341                  saveValueName);
342
343     }
344
345     public List JavaDoc unique (List JavaDoc objs)
346     {
347
348     /**
349        Strangely the method below is consistently slower than the method employed!
350     return new ArrayList (new java.util.LinkedHashSet (objs));
351     */

352
353     Map JavaDoc m = new LinkedHashMap JavaDoc ();
354
355     int s = objs.size ();
356
357     for (int i = 0; i < s; i++)
358     {
359
360         m.put (objs.get (i),
361            null);
362
363     }
364
365     return new ArrayList JavaDoc (m.keySet ());
366
367     }
368
369     public List JavaDoc unique (Expression exp)
370                     throws QueryExecutionException
371     {
372
373     return this.unique ((List JavaDoc) this.q.getVariable (Query.ALL_OBJS_VAR_NAME),
374                 exp);
375
376     }
377
378     public List JavaDoc unique (List JavaDoc objs,
379             Expression exp)
380                     throws QueryExecutionException
381     {
382
383     /**
384        Strangely the method below is consistently slower than the method employed!
385     return new ArrayList (new java.util.LinkedHashSet (objs));
386     */

387
388     Map JavaDoc m = new HashMap JavaDoc ();
389
390     int s = objs.size ();
391
392     for (int i = 0; i < s; i++)
393     {
394
395         Object JavaDoc o = objs.get (i);
396
397         o = exp.getValue (o,
398                   this.q);
399
400         m.put (o,
401            null);
402
403     }
404
405     return new ArrayList JavaDoc (m.keySet ());
406
407     }
408
409     public List JavaDoc collect (List JavaDoc objs,
410              Expression exp,
411              String JavaDoc saveValueName)
412                          throws QueryExecutionException
413     {
414
415     if (saveValueName != null)
416     {
417
418         Object JavaDoc o = this.q.getSaveValue (saveValueName);
419
420         if (o != null)
421         {
422
423         return (List JavaDoc) o;
424
425         }
426
427     }
428
429     List JavaDoc retVals = new ArrayList JavaDoc ();
430
431     int s = objs.size ();
432
433     Object JavaDoc co = this.q.getCurrentObject ();
434
435     for (int i = 0; i < s; i++)
436     {
437
438         Object JavaDoc o = objs.get (i);
439
440         this.q.setCurrentObject (o);
441
442         Object JavaDoc v = null;
443
444         // Execute the function.
445
try
446         {
447
448         retVals.add (exp.getValue (o,
449                        this.q));
450
451         } catch (Exception JavaDoc e) {
452
453         throw new QueryExecutionException ("Unable to execute expression: \"" +
454                            exp +
455                            " on object at index: " +
456                            i +
457                            " from the list of objects.",
458                            e);
459
460         }
461
462     }
463
464     if (saveValueName != null)
465     {
466
467         this.q.setSaveValue (saveValueName,
468                  retVals);
469
470     }
471
472     // Reset the current object.
473
this.q.setCurrentObject (co);
474
475     return retVals;
476
477     }
478
479     public List JavaDoc collect (Expression exp)
480                      throws QueryExecutionException
481     {
482
483     return this.collect ((List JavaDoc) this.q.getVariable (Query.ALL_OBJS_VAR_NAME),
484                 exp);
485
486     }
487
488     public List JavaDoc collect (List JavaDoc allobjs,
489              Expression exp)
490                          throws QueryExecutionException
491     {
492
493     return this.collect (allobjs,
494                  exp,
495                  null);
496
497     }
498
499     public List JavaDoc toList (Expression exp)
500                     throws QueryExecutionException
501     {
502
503     return this.toList ((List JavaDoc) this.q.getVariable (Query.ALL_OBJS_VAR_NAME),
504                 exp);
505
506     }
507
508     public List JavaDoc toList (List JavaDoc allobjs,
509             Expression exp)
510                         throws QueryExecutionException
511     {
512
513     return this.collect (allobjs,
514                  exp,
515                  null);
516
517     }
518
519     public List JavaDoc foreach (Expression exp)
520                      throws QueryExecutionException
521     {
522
523     return this.foreach ((List JavaDoc) this.q.getVariable (Query.ALL_OBJS_VAR_NAME),
524                  exp);
525
526     }
527
528     public List JavaDoc foreach (List JavaDoc allobjs,
529              Expression exp)
530                      throws QueryExecutionException
531     {
532
533     if (allobjs == null)
534     {
535
536         return null;
537
538     }
539
540     List JavaDoc res = new ArrayList JavaDoc ();
541
542     int s = allobjs.size ();
543
544     for (int i = 0; i < s; i++)
545     {
546
547         Object JavaDoc o = allobjs.get (i);
548
549         this.q.setCurrentObject (o);
550
551         res.add (exp.getValue (o,
552                    this.q));
553
554     }
555
556     return res;
557
558     }
559
560     private void fillMap (Object JavaDoc o,
561               Getter k,
562               Getter v)
563                           throws QueryExecutionException
564     {
565
566     
567
568     }
569
570     /*
571     public Map toMap (Object o,
572               Getter k,
573               Getter v)
574                   throws QueryExecutionException
575     {
576
577
578
579     }
580
581     public Map toMap (Object o,
582               Getter k,
583               Getter v,
584               String type)
585                   throws QueryExecutionException
586     {
587
588     // Try and create the correct "Map" type.
589
590     }
591     */

592
593     /**
594      * Given a list of objects, execute the expression against each one and return
595      * those objects that return a <code>true</code> value for the expression.
596      * In effect this is equivalent to executing the WHERE clause of a JoSQL statement
597      * against each object (which in fact is what happens internally). The class
598      * for the objects if found by examining the list passed in.
599      *
600      * @param objs The list of objects.
601      * @param exp The expression (basically a where clause, it is ok for the expression
602      * to start with "WHERE", case-insensitive) to execute for each of the
603      * objects.
604      * @return The list of matching objects.
605      */

606     public List JavaDoc foreach (List JavaDoc objs,
607              String JavaDoc exp)
608                      throws QueryExecutionException
609     {
610
611     List JavaDoc l = new ArrayList JavaDoc ();
612
613     if ((objs == null)
614         ||
615         (objs.size () == 0)
616        )
617     {
618
619         return l;
620
621     }
622
623     Query q = null;
624
625     // See if we have the expression in our cache.
626
if (this.foreachQueryCache != null)
627     {
628
629         q = (Query) this.foreachQueryCache.get (exp);
630
631     }
632
633     if (q == null)
634     {
635
636         // Init our query.
637
Class JavaDoc c = null;
638
639         Object JavaDoc o = objs.get (0);
640
641         if (o == null)
642         {
643
644         int s = objs.size () - 1;
645
646         // Bugger now need to cycle until we get a class.
647
for (int i = s; s > -1; i--)
648         {
649
650             o = objs.get (i);
651
652             if (o != null)
653             {
654
655             c = o.getClass ();
656             break;
657
658             }
659
660         }
661
662         } else {
663             
664         c = o.getClass ();
665             
666         }
667
668         if (exp.toLowerCase ().trim ().startsWith ("where"))
669         {
670
671         exp = exp.trim ().substring (5);
672
673         }
674
675         String JavaDoc query = "SELECT * FROM " + c.getName () + " WHERE " + exp;
676
677         q = new Query ();
678
679         try
680         {
681
682         q.parse (query);
683
684         } catch (Exception JavaDoc e) {
685
686         throw new QueryExecutionException ("Unable to create statement using WHERE clause: " +
687                            exp +
688                            " and class: " +
689                            c.getName () +
690                            " (gained from objects in list passed in)",
691                            e);
692
693         }
694
695         // Cache it.
696
if (this.foreachQueryCache == null)
697         {
698
699         this.foreachQueryCache = new HashMap JavaDoc ();
700
701         }
702
703         this.foreachQueryCache.put (exp,
704                     q);
705
706     }
707
708     return q.execute (objs).getResults ();
709
710     }
711
712     public List JavaDoc foreach (Expression listFunction,
713              Expression exp)
714                      throws QueryExecutionException
715     {
716
717     // Execute the list function.
718
Object JavaDoc o = listFunction.getValue (this.q.getCurrentObject (),
719                       this.q);
720
721     if (!(o instanceof List JavaDoc))
722     {
723
724         throw new QueryExecutionException ("Expected expression: " +
725                            listFunction +
726                            " to return instance of: " +
727                            List JavaDoc.class.getName () +
728                            " but returned instance of: " +
729                            o.getClass ().getName ());
730
731     }
732
733     List JavaDoc l = (List JavaDoc) o;
734
735     return this.foreach (l,
736                  exp);
737
738     }
739
740     /**
741      * Find objects from the List based upon the expression passed in. If
742      * the expression evaluates to <code>true</code> then the object will
743      * be returned.
744      * Note: in accordance with the general operating methodology for the Query
745      * object, the ":_allobjs" special bind variable will be set to the
746      * the List passed in and the "_currobj" will be set to the relevant
747      * object in the List.
748      *
749      * @param objs The List of objects to search.
750      * @param exp The expression to evaulate against each object in the List.
751      * @return The List of matching objects, if none match then an empty list is returned.
752      * @throws QueryExecutionException If the expression cannot be evaulated against each
753      * object.
754      */

755     public List JavaDoc find (List JavaDoc objs,
756               Expression exp)
757                   throws QueryExecutionException
758     {
759
760     // Get the current object, it's important that we leave the Query in the
761
// same state at the end of this function as when we started!
762
Object JavaDoc currobj = this.q.getCurrentObject ();
763     List JavaDoc allobjs = (List JavaDoc) this.q.getVariable (Query.ALL_OBJS_VAR_NAME);
764
765     this.q.setAllObjects (objs);
766
767     List JavaDoc r = new ArrayList JavaDoc ();
768
769     int s = objs.size ();
770
771     for (int i = 0; i < s; i++)
772     {
773
774         Object JavaDoc o = objs.get (i);
775
776         this.q.setCurrentObject (o);
777
778         try
779         {
780
781         if (exp.isTrue (o,
782                 this.q))
783         {
784
785             r.add (o);
786
787         }
788
789         } catch (Exception JavaDoc e) {
790
791         throw new QueryExecutionException ("Unable to evaulate expression: " +
792                            exp +
793                            " against object: " +
794                            i +
795                            " (class: " +
796                            o.getClass ().getName () +
797                            ")",
798                            e);
799
800         }
801
802     }
803
804     // Restore the currobj and allobjs.
805
this.q.setCurrentObject (currobj);
806     this.q.setAllObjects (allobjs);
807
808     return r;
809
810     }
811
812     /**
813      * Group objects from the List based upon the expression passed in. The expression
814      * is evaulated for each object, by calling: {@link Expression#getValue(Object,Query)}
815      * and the return value used as the key to the Map. All objects with that value are
816      * added to a List held against the key. To maintain the ordering of the keys (if
817      * desirable) a {@link LinkedHashMap} is used as the return Map.
818      *
819      * Note: in accordance with the general operating methodology for the Query
820      * object, the ":_allobjs" special bind variable will be set to the
821      * the List passed in and the "_currobj" will be set to the relevant
822      * object in the List.
823      *
824      * @param objs The List of objects to search.
825      * @param exp The expression to evaulate against each object in the List.
826      * @return The LinkedHashMap of matching objects, grouped according to the return value
827      * of executing the expression against each object in the input List.
828      * @throws QueryExecutionException If the expression cannot be evaulated against each
829      * object.
830      */

831     public Map JavaDoc grp (List JavaDoc objs,
832             Expression exp)
833                 throws QueryExecutionException
834     {
835     
836     // Get the current object, it's important that we leave the Query in the
837
// same state at the end of this function as when we started!
838
Object JavaDoc currobj = this.q.getCurrentObject ();
839     List JavaDoc allobjs = (List JavaDoc) this.q.getVariable (Query.ALL_OBJS_VAR_NAME);
840
841     this.q.setAllObjects (objs);
842
843     Map JavaDoc r = new LinkedHashMap JavaDoc ();
844
845     int s = objs.size ();
846
847     for (int i = 0; i < s; i++)
848     {
849
850         Object JavaDoc o = objs.get (i);
851
852         this.q.setCurrentObject (o);
853
854         try
855         {
856
857         Object JavaDoc v = exp.getValue (o,
858                      this.q);
859
860         List JavaDoc vs = (List JavaDoc) r.get (v);
861
862         if (vs == null)
863         {
864
865             vs = new ArrayList JavaDoc ();
866
867             r.put (v,
868                vs);
869
870         }
871
872         vs.add (v);
873
874         } catch (Exception JavaDoc e) {
875
876         throw new QueryExecutionException ("Unable to evaulate expression: " +
877                            exp +
878                            " against object: " +
879                            i +
880                            " (class: " +
881                            o.getClass ().getName () +
882                            ")",
883                            e);
884
885         }
886
887     }
888
889     // Restore the currobj and allobjs.
890
this.q.setCurrentObject (currobj);
891     this.q.setAllObjects (allobjs);
892
893     return r;
894
895     }
896
897 }
898
Popular Tags