KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > freemarker > core > SequenceBuiltins


1 /*
2  * Copyright (c) 2003 The Visigoth Software Society. All rights
3  * reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  *
17  * 3. The end-user documentation included with the redistribution, if
18  * any, must include the following acknowledgement:
19  * "This product includes software developed by the
20  * Visigoth Software Society (http://www.visigoths.org/)."
21  * Alternately, this acknowledgement may appear in the software itself,
22  * if and wherever such third-party acknowledgements normally appear.
23  *
24  * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
25  * project contributors may be used to endorse or promote products derived
26  * from this software without prior written permission. For written
27  * permission, please contact visigoths@visigoths.org.
28  *
29  * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
30  * nor may "FreeMarker" or "Visigoth" appear in their names
31  * without prior written permission of the Visigoth Software Society.
32  *
33  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
34  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36  * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
37  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
39  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
40  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
41  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
42  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
43  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44  * SUCH DAMAGE.
45  * ====================================================================
46  *
47  * This software consists of voluntary contributions made by many
48  * individuals on behalf of the Visigoth Software Society. For more
49  * information on the Visigoth Software Society, please see
50  * http://www.visigoths.org/
51  */

52
53 package freemarker.core;
54
55 import java.text.Collator JavaDoc;
56 import java.util.ArrayList JavaDoc;
57 import java.util.Collections JavaDoc;
58 import java.util.Comparator JavaDoc;
59 import java.util.Date JavaDoc;
60 import java.util.List JavaDoc;
61
62 import freemarker.template.SimpleNumber;
63 import freemarker.template.TemplateBooleanModel;
64 import freemarker.template.TemplateDateModel;
65 import freemarker.template.TemplateException;
66 import freemarker.template.TemplateHashModel;
67 import freemarker.template.TemplateMethodModelEx;
68 import freemarker.template.TemplateModel;
69 import freemarker.template.TemplateModelException;
70 import freemarker.template.TemplateModelListSequence;
71 import freemarker.template.TemplateNumberModel;
72 import freemarker.template.TemplateScalarModel;
73 import freemarker.template.TemplateSequenceModel;
74 import freemarker.template.utility.Constants;
75 import freemarker.template.utility.StringUtil;
76
77 /**
78  * A holder for builtins that operate exclusively on TemplateSequenceModels.
79  */

80
81 abstract class SequenceBuiltins {
82     abstract static class SequenceBuiltIn extends BuiltIn {
83         TemplateModel _getAsTemplateModel(Environment env)
84                 throws TemplateException
85         {
86             TemplateModel model = target.getAsTemplateModel(env);
87             if (!(model instanceof TemplateSequenceModel)) {
88                 throw invalidTypeException(model, target, env, "sequence");
89             }
90             return calculateResult((TemplateSequenceModel) model);
91         }
92         abstract TemplateModel calculateResult(TemplateSequenceModel tsm)
93         throws
94             TemplateModelException;
95     }
96
97     static class firstBI extends SequenceBuiltIn {
98         TemplateModel calculateResult(TemplateSequenceModel tsm)
99         throws
100             TemplateModelException
101         {
102             if (tsm.size() == 0) {
103                 return null;
104             }
105             return tsm.get(0);
106         }
107     }
108
109     static class lastBI extends SequenceBuiltIn {
110         TemplateModel calculateResult(TemplateSequenceModel tsm)
111         throws
112             TemplateModelException
113         {
114             if (tsm.size() == 0) {
115                 return null;
116             }
117             return tsm.get(tsm.size() -1);
118         }
119     }
120
121     static class reverseBI extends SequenceBuiltIn {
122         TemplateModel calculateResult(TemplateSequenceModel tsm) {
123             if (tsm instanceof ReverseSequence) {
124                 return ((ReverseSequence) tsm).seq;
125             } else {
126                 return new ReverseSequence(tsm);
127             }
128         }
129
130         private static class ReverseSequence implements TemplateSequenceModel
131         {
132             private final TemplateSequenceModel seq;
133
134             ReverseSequence(TemplateSequenceModel seq)
135             {
136                 this.seq = seq;
137             }
138
139             public int size() throws TemplateModelException
140             {
141                 return seq.size();
142             }
143
144             public TemplateModel get(int index) throws TemplateModelException
145             {
146                 return seq.get(seq.size() - 1 - index);
147             }
148         }
149     }
150
151     static class sortBI extends SequenceBuiltIn {
152         
153         static final int KEY_TYPE_STRING = 1;
154         static final int KEY_TYPE_NUMBER = 2;
155         static final int KEY_TYPE_DATE = 3;
156         
157         TemplateModel calculateResult(TemplateSequenceModel seq)
158                 throws TemplateModelException {
159             return sort(seq, null);
160         }
161         
162         static String JavaDoc startErrorMessage(Object JavaDoc keys) {
163             return (keys == null ? "?sort" : "?sort_by(...)") + " failed: ";
164         }
165         
166         /**
167          * Sorts a sequence for the <tt>sort</tt> and <tt>sort_by</tt>
168          * built-ins.
169          *
170          * @param seq the sequence to sort.
171          * @param keys the name of the subvariable whose value is used for the
172          * sorting. If the sorting is done by a sub-subvaruable, then this
173          * will be of length 2, and so on. If the sorting is done by the
174          * sequene items directly, then this argument has to be 0 length
175          * array or <code>null</code>.
176          * @return a new sorted sequence, or the original sequence if the
177          * sequence length was 0.
178          */

179         static TemplateSequenceModel sort(TemplateSequenceModel seq, String JavaDoc[] keys)
180                 throws TemplateModelException {
181             int i;
182             int keyCnt;
183
184             int ln = seq.size();
185             if (ln == 0) {
186                 return seq;
187             }
188             
189             List JavaDoc res = new ArrayList JavaDoc(ln);
190             Object JavaDoc item;
191             item = seq.get(0);
192             if (keys != null) {
193                 keyCnt = keys.length;
194                 if (keyCnt == 0) {
195                     keys = null;
196                 } else {
197                     for (i = 0; i < keyCnt; i++) {
198                         if (!(item instanceof TemplateHashModel)) {
199                             throw new TemplateModelException(
200                                     startErrorMessage(keys)
201                                     + (i == 0
202                                             ? "You can't use ?sort_by when the "
203                                               + "sequence items are not hashes."
204                                             : "The subvariable "
205                                               + StringUtil.jQuote(keys[i - 1])
206                                               + " is not a hash, so ?sort_by "
207                                               + "can't proceed by getting the "
208                                               + StringUtil.jQuote(keys[i])
209                                               + " subvariable."));
210                         }
211                         
212                         item = ((TemplateHashModel) item).get(keys[i]);
213                         if (item == null) {
214                             throw new TemplateModelException(
215                                     startErrorMessage(keys)
216                                     + "The " + StringUtil.jQuote(keys[i])
217                                     + " subvariable "
218                                     + (keyCnt == 1
219                                         ? "was not found."
220                                         : "(specified by ?sort_by argument number "
221                                           + (i + 1) + ") was not found."));
222                         }
223                     }
224                 }
225             } else {
226                 keyCnt = 0;
227             }
228
229             int keyType;
230             if (item instanceof TemplateScalarModel) {
231                 keyType = KEY_TYPE_STRING;
232             } else if (item instanceof TemplateNumberModel) {
233                 keyType = KEY_TYPE_NUMBER;
234             } else if (item instanceof TemplateDateModel) {
235                 keyType = KEY_TYPE_DATE;
236             } else {
237                 throw new TemplateModelException(
238                         startErrorMessage(keys)
239                         + "Values used for sorting must be numbers, strings, or date/time values.");
240             }
241
242             if (keys == null) {
243                 if (keyType == KEY_TYPE_STRING) {
244                     for (i = 0; i < ln; i++) {
245                         item = seq.get(i);
246                         try {
247                             res.add(new KVP(
248                                     ((TemplateScalarModel) item).getAsString(),
249                                     item));
250                         } catch (ClassCastException JavaDoc e) {
251                             if (!(item instanceof TemplateScalarModel)) {
252                                 throw new TemplateModelException(
253                                         startErrorMessage(keys)
254                                         + "All values in the sequence must be "
255                                         + "strings, because the first value "
256                                         + "was a string. "
257                                         + "The value at index " + i
258                                         + " is not string.");
259                             } else {
260                                 throw e;
261                             }
262                         }
263                     }
264                 } else if (keyType == KEY_TYPE_NUMBER) {
265                     for (i = 0; i < ln; i++) {
266                         item = seq.get(i);
267                         try {
268                             res.add(new KVP(
269                                     ((TemplateNumberModel) item).getAsNumber(),
270                                     item));
271                         } catch (ClassCastException JavaDoc e) {
272                             if (!(item instanceof TemplateNumberModel)) {
273                                 throw new TemplateModelException(
274                                         startErrorMessage(keys)
275                                         + "All values in the sequence must be "
276                                         + "numbers, because the first value "
277                                         + "was a number. "
278                                         + "The value at index " + i
279                                         + " is not number.");
280                             } else {
281                                 throw e;
282                             }
283                         }
284                     }
285                 } else if (keyType == KEY_TYPE_DATE) {
286                     for (i = 0; i < ln; i++) {
287                         item = seq.get(i);
288                         try {
289                             res.add(new KVP(
290                                     ((TemplateDateModel) item).getAsDate(),
291                                     item));
292                         } catch (ClassCastException JavaDoc e) {
293                             if (!(item instanceof TemplateNumberModel)) {
294                                 throw new TemplateModelException(
295                                         startErrorMessage(keys)
296                                         + "All values in the sequence must be "
297                                         + "date/time values, because the first "
298                                         + "value was a date/time. "
299                                         + "The value at index " + i
300                                         + " is not date/time.");
301                             } else {
302                                 throw e;
303                             }
304                         }
305                     }
306                 } else {
307                     throw new RuntimeException JavaDoc("FreeMarker bug: Bad key type");
308                 }
309             } else {
310                 for (i = 0; i < ln; i++) {
311                     item = seq.get(i);
312                     Object JavaDoc key = item;
313                     for (int j = 0; j < keyCnt; j++) {
314                         try {
315                             key = ((TemplateHashModel) key).get(keys[j]);
316                         } catch (ClassCastException JavaDoc e) {
317                             if (!(key instanceof TemplateHashModel)) {
318                                 throw new TemplateModelException(
319                                         startErrorMessage(keys)
320                                         + "Problem with the sequence item at index " + i + ": "
321                                         + "Can't get the " + StringUtil.jQuote(keys[j])
322                                         + " subvariable, because the value is not a hash.");
323                             } else {
324                                 throw e;
325                             }
326                         }
327                         if (key == null) {
328                             throw new TemplateModelException(
329                                     startErrorMessage(keys)
330                                     + "Problem with the sequence item at index " + i + ": "
331                                     + "The " + StringUtil.jQuote(keys[j])
332                                     + " subvariable was not found.");
333                         }
334                     }
335                     if (keyType == KEY_TYPE_STRING) {
336                         try {
337                             res.add(new KVP(
338                                     ((TemplateScalarModel) key).getAsString(),
339                                     item));
340                         } catch (ClassCastException JavaDoc e) {
341                             if (!(key instanceof TemplateScalarModel)) {
342                                 throw new TemplateModelException(
343                                         startErrorMessage(keys)
344                                         + "All key values in the sequence must be "
345                                         + "date/time values, because the first key "
346                                         + "value was a date/time. The key value at "
347                                         + "index " + i + " is not a date/time.");
348                             } else {
349                                 throw e;
350                             }
351                         }
352                     } else if (keyType == KEY_TYPE_NUMBER) {
353                         try {
354                             res.add(new KVP(
355                                     ((TemplateNumberModel) key).getAsNumber(),
356                                     item));
357                         } catch (ClassCastException JavaDoc e) {
358                             if (!(key instanceof TemplateNumberModel)) {
359                                 throw new TemplateModelException(
360                                         startErrorMessage(keys)
361                                         + "All key values in the sequence must be "
362                                         + "numbers, because the first key "
363                                         + "value was a number. The key value at "
364                                         + "index " + i + " is not a number.");
365                             }
366                         }
367                     } else if (keyType == KEY_TYPE_DATE) {
368                         try {
369                             res.add(new KVP(
370                                     ((TemplateDateModel) key).getAsDate(),
371                                     item));
372                         } catch (ClassCastException JavaDoc e) {
373                             if (!(key instanceof TemplateDateModel)) {
374                                 throw new TemplateModelException(
375                                         startErrorMessage(keys)
376                                         + "All key values in the sequence must be "
377                                         + "dates, because the first key "
378                                         + "value was a date. The key value at "
379                                         + "index " + i + " is not a date.");
380                             }
381                         }
382                     } else {
383                         throw new RuntimeException JavaDoc("FreeMarker bug: Bad key type");
384                     }
385                 }
386             }
387
388             Comparator JavaDoc cmprtr;
389             if (keyType == KEY_TYPE_STRING) {
390                 cmprtr = new LexicalKVPComparator(
391                         Environment.getCurrentEnvironment().getCollator());
392             } else if (keyType == KEY_TYPE_NUMBER) {
393                 cmprtr = new NumericalKVPComparator(
394                         Environment.getCurrentEnvironment()
395                                 .getArithmeticEngine());
396             } else if (keyType == KEY_TYPE_DATE) {
397                 cmprtr = new DateKVPComparator();
398             } else {
399                 throw new RuntimeException JavaDoc("FreeMarker bug: Bad key type");
400             }
401
402             try {
403                 Collections.sort(res, cmprtr);
404             } catch (ClassCastException JavaDoc exc) {
405                 throw new TemplateModelException(
406                         startErrorMessage(keys)
407                         + "Unexpected error while sorting:" + exc, exc);
408             }
409
410             for (i = 0; i < ln; i++) {
411                 res.set(i, ((KVP) res.get(i)).value);
412             }
413
414             return new TemplateModelListSequence(res);
415         }
416
417         private static class KVP {
418             private KVP(Object JavaDoc key, Object JavaDoc value) {
419                 this.key = key;
420                 this.value = value;
421             }
422
423             private Object JavaDoc key;
424             private Object JavaDoc value;
425         }
426
427         private static class NumericalKVPComparator implements Comparator JavaDoc {
428             private ArithmeticEngine ae;
429
430             private NumericalKVPComparator(ArithmeticEngine ae) {
431                 this.ae = ae;
432             }
433
434             public int compare(Object JavaDoc arg0, Object JavaDoc arg1) {
435                 try {
436                     return ae.compareNumbers(
437                             (Number JavaDoc) ((KVP) arg0).key,
438                             (Number JavaDoc) ((KVP) arg1).key);
439                 } catch (TemplateException e) {
440                     throw new ClassCastException JavaDoc(
441                         "Failed to compare numbers: " + e);
442                 }
443             }
444         }
445
446         private static class LexicalKVPComparator implements Comparator JavaDoc {
447             private Collator JavaDoc collator;
448
449             LexicalKVPComparator(Collator JavaDoc collator) {
450                 this.collator = collator;
451             }
452
453             public int compare(Object JavaDoc arg0, Object JavaDoc arg1) {
454                 return collator.compare(
455                         ((KVP) arg0).key, ((KVP) arg1).key);
456             }
457         }
458         
459         private static class DateKVPComparator implements Comparator JavaDoc {
460
461             public int compare(Object JavaDoc arg0, Object JavaDoc arg1) {
462                 return ((Date JavaDoc) ((KVP) arg0).key).compareTo(
463                         (Date JavaDoc) ((KVP) arg1).key);
464             }
465         }
466         
467     }
468     
469     static class sort_byBI extends sortBI {
470         TemplateModel calculateResult(TemplateSequenceModel seq)
471         {
472             return new BIMethod(seq);
473         }
474         
475         static class BIMethod implements TemplateMethodModelEx {
476             TemplateSequenceModel seq;
477             
478             BIMethod(TemplateSequenceModel seq) {
479                 this.seq = seq;
480             }
481             
482             public Object JavaDoc exec(List JavaDoc params)
483                     throws TemplateModelException {
484                 if (params.size() == 0) {
485                     throw new TemplateModelException(
486                             "?sort_by(key) needs exactly 1 argument.");
487                 }
488                 String JavaDoc[] subvars;
489                 Object JavaDoc obj = params.get(0);
490                 if (obj instanceof TemplateScalarModel) {
491                     subvars = new String JavaDoc[]{((TemplateScalarModel) obj).getAsString()};
492                 } else if (obj instanceof TemplateSequenceModel) {
493                     TemplateSequenceModel seq = (TemplateSequenceModel) obj;
494                     int ln = seq.size();
495                     subvars = new String JavaDoc[ln];
496                     for (int i = 0; i < ln; i++) {
497                         Object JavaDoc item = seq.get(i);
498                         try {
499                             subvars[i] = ((TemplateScalarModel) item)
500                                     .getAsString();
501                         } catch (ClassCastException JavaDoc e) {
502                             if (!(item instanceof TemplateScalarModel)) {
503                                 throw new TemplateModelException(
504                                         "The argument to ?sort_by(key), when it "
505                                         + "is a sequence, must be a sequence of "
506                                         + "strings, but the item at index " + i
507                                         + " is not a string." );
508                             }
509                         }
510                     }
511                 } else {
512                     throw new TemplateModelException(
513                             "The argument to ?sort_by(key) must be a string "
514                             + "(the name of the subvariable), or a sequence of "
515                             + "strings (the \"path\" to the subvariable).");
516                 }
517                 return sort(seq, subvars);
518             }
519         }
520     }
521
522     static class seq_containsBI extends BuiltIn {
523         TemplateModel _getAsTemplateModel(Environment env)
524                 throws TemplateException {
525             TemplateModel model = target.getAsTemplateModel(env);
526             if (!(model instanceof TemplateSequenceModel))
527                 throw invalidTypeException(model, target, env, "sequence");
528             return new BIMethod((TemplateSequenceModel) model, env);
529         }
530
531         private class BIMethod implements TemplateMethodModelEx {
532             private TemplateSequenceModel m_seq;
533             private Environment m_env;
534
535             private BIMethod(TemplateSequenceModel seq, Environment env) {
536                 m_seq = seq;
537                 m_env = env;
538             }
539
540             public Object JavaDoc exec(List JavaDoc args)
541                     throws TemplateModelException {
542                 if (args.size() != 1)
543                     throw new TemplateModelException("?seq_contains(...) expects one argument.");
544                 TemplateModel arg = (TemplateModel) args.get(0);
545                 int size = m_seq.size();
546                 for (int i = 0; i < size; i++) {
547                     if (modelsEqual(m_seq.get(i), arg, m_env))
548                         return TemplateBooleanModel.TRUE;
549                 }
550                 return TemplateBooleanModel.FALSE;
551             }
552
553         }
554     }
555
556     static class seq_index_ofBI extends BuiltIn {
557         private int m_dir;
558
559         public seq_index_ofBI(int dir) {
560             m_dir = dir;
561         }
562
563         TemplateModel _getAsTemplateModel(Environment env)
564                 throws TemplateException {
565             TemplateModel model = target.getAsTemplateModel(env);
566             if (!(model instanceof TemplateSequenceModel))
567                 throw invalidTypeException(model, target, env, "sequence");
568             return new BIMethod((TemplateSequenceModel) model, env);
569         }
570
571         private class BIMethod implements TemplateMethodModelEx {
572             private TemplateSequenceModel m_seq;
573             private Environment m_env;
574
575             private BIMethod(TemplateSequenceModel seq, Environment env) {
576                 m_seq = seq;
577                 m_env = env;
578             }
579
580             public Object JavaDoc exec(List JavaDoc args)
581                     throws TemplateModelException {
582                 int argcnt = args.size();
583                 if (argcnt != 1 && argcnt != 2) {
584                     throw new TemplateModelException(
585                             getBuiltinTemplate() + " expects 1 or 2 arguments.");
586                 }
587                 
588                 int startIndex;
589                 int seqSize = m_seq.size();
590                 TemplateModel arg = (TemplateModel) args.get(0);
591                 if (argcnt > 1) {
592                     Object JavaDoc obj = args.get(1);
593                     if (!(obj instanceof TemplateNumberModel)) {
594                         throw new TemplateModelException(
595                                 getBuiltinTemplate()
596                                 + "expects a number as its second argument.");
597                     }
598                     startIndex = ((TemplateNumberModel) obj).getAsNumber().intValue();
599                     if (m_dir == 1) {
600                         if (startIndex >= seqSize) {
601                             return Constants.MINUS_ONE;
602                         }
603                         if (startIndex < 0) {
604                             startIndex = 0;
605                         }
606                     } else {
607                         if (startIndex >= seqSize) {
608                             startIndex = seqSize - 1;
609                         }
610                         if (startIndex < 0) {
611                             return Constants.MINUS_ONE;
612                         }
613                     }
614                 } else {
615                     if (m_dir == 1) {
616                         startIndex = 0;
617                     } else {
618                         startIndex = seqSize - 1;
619                     }
620                 }
621                 
622                 if (m_dir == 1) {
623                     for (int i = startIndex; i < seqSize; i++) {
624                         if (modelsEqual(m_seq.get(i), arg, m_env))
625                             return new SimpleNumber(i);
626                     }
627                 } else {
628                     for (int i = startIndex; i >= 0; i--) {
629                         if (modelsEqual(m_seq.get(i), arg, m_env))
630                             return new SimpleNumber(i);
631                     }
632                 }
633                 return Constants.MINUS_ONE;
634             }
635             
636             private String JavaDoc getBuiltinTemplate() {
637                     if (m_dir == 1)
638                         return "?seq_indexOf(...)";
639                     else
640                         return "?seq_lastIndexOf(...)";
641             }
642         }
643     }
644
645     static class chunkBI extends SequenceBuiltIn {
646
647         TemplateModel calculateResult(TemplateSequenceModel tsm) throws TemplateModelException {
648             return new BIMethod(tsm);
649         }
650
651         private static class BIMethod implements TemplateMethodModelEx {
652             
653             private final TemplateSequenceModel tsm;
654
655             private BIMethod(TemplateSequenceModel tsm) {
656                 this.tsm = tsm;
657             }
658
659             public Object JavaDoc exec(List JavaDoc args) throws TemplateModelException {
660                 int numArgs = args.size();
661                 if (numArgs != 1 && numArgs !=2) {
662                     throw new TemplateModelException(
663                             "?chunk(...) expects 1 or 2 arguments.");
664                 }
665                 
666                 Object JavaDoc chunkSize = args.get(0);
667                 if (!(chunkSize instanceof TemplateNumberModel)) {
668                     throw new TemplateModelException(
669                             "?chunk(...) expects a number as "
670                             + "its 1st argument.");
671                 }
672                 
673                 return new ChunkedSequence(
674                         tsm,
675                         ((TemplateNumberModel) chunkSize).getAsNumber().intValue(),
676                         numArgs > 1 ? (TemplateModel) args.get(1) : null);
677             }
678         }
679         
680         private static class ChunkedSequence implements TemplateSequenceModel {
681             
682             private final TemplateSequenceModel wrappedTsm;
683             
684             private final int chunkSize;
685             
686             private final TemplateModel fillerItem;
687             
688             private final int numberOfChunks;
689             
690             private ChunkedSequence(
691                     TemplateSequenceModel wrappedTsm, int chunkSize, TemplateModel fillerItem)
692                     throws TemplateModelException {
693                 if (chunkSize < 1) {
694                     throw new TemplateModelException(
695                             "The 1st argument to ?chunk(...) must be at least 1.");
696                 }
697                 this.wrappedTsm = wrappedTsm;
698                 this.chunkSize = chunkSize;
699                 this.fillerItem = fillerItem;
700                 numberOfChunks = (wrappedTsm.size() + chunkSize - 1) / chunkSize;
701             }
702
703             public TemplateModel get(final int chunkIndex)
704                     throws TemplateModelException {
705                 if (chunkIndex >= numberOfChunks) {
706                     return null;
707                 }
708                 
709                 return new TemplateSequenceModel() {
710                     
711                     private final int baseIndex = chunkIndex * chunkSize;
712
713                     public TemplateModel get(int relIndex)
714                             throws TemplateModelException {
715                         int absIndex = baseIndex + relIndex;
716                         if (absIndex < wrappedTsm.size()) {
717                             return wrappedTsm.get(absIndex);
718                         } else {
719                             return absIndex < numberOfChunks * chunkSize
720                                 ? fillerItem
721                                 : null;
722                         }
723                     }
724
725                     public int size() throws TemplateModelException {
726                         return fillerItem != null || chunkIndex + 1 < numberOfChunks
727                                 ? chunkSize
728                                 : wrappedTsm.size() - baseIndex;
729                     }
730                     
731                 };
732             }
733
734             public int size() throws TemplateModelException {
735                 return numberOfChunks;
736             }
737             
738         }
739         
740     }
741     
742     /*
743      * WARNING! This algorithm is duplication of ComparisonExpression.isTrue(...).
744      * Thus, if you update this method, then you have to update that too!
745      */

746     public static boolean modelsEqual(TemplateModel model1, TemplateModel model2,
747                                 Environment env)
748             throws TemplateModelException {
749         if (env.isClassicCompatible()) {
750             if (model1 == null) {
751                 model1 = TemplateScalarModel.EMPTY_STRING;
752             }
753             if (model2 == null) {
754                 model2 = TemplateScalarModel.EMPTY_STRING;
755             }
756         }
757
758         int comp = -1;
759         if(model1 instanceof TemplateNumberModel && model2 instanceof TemplateNumberModel) {
760             Number JavaDoc first = ((TemplateNumberModel) model1).getAsNumber();
761             Number JavaDoc second = ((TemplateNumberModel) model2).getAsNumber();
762             ArithmeticEngine ae =
763                 env != null
764                     ? env.getArithmeticEngine()
765                     : env.getTemplate().getArithmeticEngine();
766             try {
767                 comp = ae.compareNumbers(first, second);
768             } catch (TemplateException ex) {
769                 throw new TemplateModelException(ex);
770             }
771         }
772         else if(model1 instanceof TemplateDateModel && model2 instanceof TemplateDateModel) {
773             TemplateDateModel ltdm = (TemplateDateModel)model1;
774             TemplateDateModel rtdm = (TemplateDateModel)model2;
775             int ltype = ltdm.getDateType();
776             int rtype = rtdm.getDateType();
777             if(ltype != rtype) {
778                 throw new TemplateModelException(
779                     "Can not compare dates of different type. Left date is of "
780                     + TemplateDateModel.TYPE_NAMES.get(ltype)
781                     + " type, right date is of "
782                     + TemplateDateModel.TYPE_NAMES.get(rtype) + " type.");
783             }
784             if(ltype == TemplateDateModel.UNKNOWN) {
785                 throw new TemplateModelException(
786                     "Left date is of UNKNOWN type, and can not be compared.");
787             }
788             if(rtype == TemplateDateModel.UNKNOWN) {
789                 throw new TemplateModelException(
790                     "Right date is of UNKNOWN type, and can not be compared.");
791             }
792             Date JavaDoc first = ltdm.getAsDate();
793             Date JavaDoc second = rtdm.getAsDate();
794             comp = first.compareTo(second);
795         }
796         else if(model1 instanceof TemplateScalarModel && model2 instanceof TemplateScalarModel) {
797             String JavaDoc first = ((TemplateScalarModel) model1).getAsString();
798             String JavaDoc second = ((TemplateScalarModel) model2).getAsString();
799             comp = env.getCollator().compare(first, second);
800         }
801         else if(model1 instanceof TemplateBooleanModel && model2 instanceof TemplateBooleanModel) {
802             boolean first = ((TemplateBooleanModel)model1).getAsBoolean();
803             boolean second = ((TemplateBooleanModel)model2).getAsBoolean();
804             comp = (first ? 1 : 0) - (second ? 1 : 0);
805         }
806
807         return (comp == 0);
808     }
809     
810 }
Popular Tags