Open Source Repository

Home /freemarker/freemarker-2.3.16 | Repository Home


freemarker/core/SequenceBuiltins.java
/*
 * Copyright (c) 2003 The Visigoth Software Society. All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowledgement:
 *       "This product includes software developed by the
 *        Visigoth Software Society (http://www.visigoths.org/)."
 *    Alternately, this acknowledgement may appear in the software itself,
 *    if and wherever such third-party acknowledgements normally appear.
 *
 * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
 *    project contributors may be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact [email protected]
 *
 * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
 *    nor may "FreeMarker" or "Visigoth" appear in their names
 *    without prior written permission of the Visigoth Software Society.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Visigoth Software Society. For more
 * information on the Visigoth Software Society, please see
 * http://www.visigoths.org/
 */

package freemarker.core;

import java.io.Serializable;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import freemarker.template.SimpleNumber;
import freemarker.template.TemplateBooleanModel;
import freemarker.template.TemplateDateModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateModelListSequence;
import freemarker.template.TemplateNumberModel;
import freemarker.template.TemplateScalarModel;
import freemarker.template.TemplateSequenceModel;
import freemarker.template.TemplateCollectionModel;
import freemarker.template.TemplateModelIterator;
import freemarker.template.utility.Constants;
import freemarker.template.utility.StringUtil;

/**
 * A holder for builtins that operate exclusively on TemplateSequenceModels.
 */

abstract class SequenceBuiltins {
    abstract static class SequenceBuiltIn extends BuiltIn {
        TemplateModel _getAsTemplateModel(Environment env)
                throws TemplateException
        {
            TemplateModel model = target.getAsTemplateModel(env);
            if (!(model instanceof TemplateSequenceModel)) {
                throw invalidTypeException(model, target, env, "sequence");
            }
            return calculateResult((TemplateSequenceModelmodel);
        }
        abstract TemplateModel calculateResult(TemplateSequenceModel tsm)
        throws
            TemplateModelException;
    }

    static class firstBI extends SequenceBuiltIn {
        TemplateModel calculateResult(TemplateSequenceModel tsm)
        throws
            TemplateModelException
        {
            if (tsm.size() == 0) {
                return null;
            }
            return tsm.get(0);
        }
    }

    static class lastBI extends SequenceBuiltIn {
        TemplateModel calculateResult(TemplateSequenceModel tsm)
        throws
            TemplateModelException
        {
            if (tsm.size() == 0) {
                return null;
            }
            return tsm.get(tsm.size() -1);
        }
    }

    static class reverseBI extends SequenceBuiltIn {
        TemplateModel calculateResult(TemplateSequenceModel tsm) {
            if (tsm instanceof ReverseSequence) {
                return ((ReverseSequencetsm).seq;
            else {
                return new ReverseSequence(tsm);
            }
        }

        private static class ReverseSequence implements TemplateSequenceModel
        {
            private final TemplateSequenceModel seq;

            ReverseSequence(TemplateSequenceModel seq)
            {
                this.seq = seq;
            }

            public int size() throws TemplateModelException
            {
                return seq.size();
            }

            public TemplateModel get(int indexthrows TemplateModelException
            {
                return seq.get(seq.size() - index);
            }
        }
    }

    static class sortBI extends SequenceBuiltIn {
        
        static final int KEY_TYPE_STRING = 1;
        static final int KEY_TYPE_NUMBER = 2;
        static final int KEY_TYPE_DATE = 3;
        
        TemplateModel calculateResult(TemplateSequenceModel seq)
                throws TemplateModelException {
            return sort(seq, null);
        }
        
        static String startErrorMessage(Object keys) {
            return (keys == null "?sort" "?sort_by(...)"" failed: ";
        }
        
        /**
         * Sorts a sequence for the <tt>sort</tt> and <tt>sort_by</tt>
         * built-ins.
         
         @param seq the sequence to sort.
         @param keys the name of the subvariable whose value is used for the
         *     sorting. If the sorting is done by a sub-subvaruable, then this
         *     will be of length 2, and so on. If the sorting is done by the
         *     sequene items directly, then this argument has to be 0 length
         *     array or <code>null</code>.
         @return a new sorted sequence, or the original sequence if the
         *     sequence length was 0.
         */
        static TemplateSequenceModel sort(TemplateSequenceModel seq, String[] keys)
                throws TemplateModelException {
            int i;
            int keyCnt;

            int ln = seq.size();
            if (ln == 0) {
                return seq;
            }
            
            List res = new ArrayList(ln);
            Object item;
            item = seq.get(0);
            if (keys != null) {
                keyCnt = keys.length;
                if (keyCnt == 0) {
                    keys = null;
                else {
                    for (i = 0; i < keyCnt; i++) {
                        if (!(item instanceof TemplateHashModel)) {
                            throw new TemplateModelException(
                                    startErrorMessage(keys)
                                    (i == 0
                                            "You can't use ?sort_by when the "
                                              "sequence items are not hashes."
                                            "The subvariable "
                                              + StringUtil.jQuote(keys[i - 1])
                                              " is not a hash, so ?sort_by "
                                              "can't proceed by getting the "
                                              + StringUtil.jQuote(keys[i])
                                              " subvariable."));
                        }
                        
                        item = ((TemplateHashModelitem).get(keys[i]);
                        if (item == null) {
                            throw new TemplateModelException(
                                    startErrorMessage(keys)
                                    "The " + StringUtil.jQuote(keys[i])
                                    " subvariable "
                                    (keyCnt == 1
                                        "was not found."
                                        "(specified by ?sort_by argument number "
                                          (i + 1") was not found."));
                        }
                    }
                }
            else {
                keyCnt = 0;
            }

            int keyType;
            if (item instanceof TemplateScalarModel) {
                keyType = KEY_TYPE_STRING;
            else if (item instanceof TemplateNumberModel) {
                keyType = KEY_TYPE_NUMBER;
            else if (item instanceof TemplateDateModel) {
                keyType = KEY_TYPE_DATE;
            else {
                throw new TemplateModelException(
                        startErrorMessage(keys)
                        "Values used for sorting must be numbers, strings, or date/time values.");
            }

            if (keys == null) {
                if (keyType == KEY_TYPE_STRING) {
                    for (i = 0; i < ln; i++) {
                        item = seq.get(i);
                        try {
                            res.add(new KVP(
                                    ((TemplateScalarModelitem).getAsString(),
                                    item));
                        catch (ClassCastException e) {
                            if (!(item instanceof TemplateScalarModel)) {
                                throw new TemplateModelException(
                                        startErrorMessage(null)
                                        "All values in the sequence must be "
                                        "strings, because the first value "
                                        "was a string. "
                                        "The value at index " + i
                                        " is not string.");
                            else {
                                throw e;
                            }
                        }
                    }
                else if (keyType == KEY_TYPE_NUMBER) {
                    for (i = 0; i < ln; i++) {
                        item = seq.get(i);
                        try {
                            res.add(new KVP(
                                    ((TemplateNumberModelitem).getAsNumber(),
                                    item));
                        catch (ClassCastException e) {
                            if (!(item instanceof TemplateNumberModel)) {
                                throw new TemplateModelException(
                                        startErrorMessage(null)
                                        "All values in the sequence must be "
                                        "numbers, because the first value "
                                        "was a number. "
                                        "The value at index " + i
                                        " is not number.");
                            else {
                                throw e;
                            }
                        }
                    }
                else if (keyType == KEY_TYPE_DATE) {
                    for (i = 0; i < ln; i++) {
                        item = seq.get(i);
                        try {
                            res.add(new KVP(
                                    ((TemplateDateModelitem).getAsDate(),
                                    item));
                        catch (ClassCastException e) {
                            if (!(item instanceof TemplateNumberModel)) {
                                throw new TemplateModelException(
                                        startErrorMessage(null)
                                        "All values in the sequence must be "
                                        "date/time values, because the first "
                                        "value was a date/time. "
                                        "The value at index " + i
                                        " is not date/time.");
                            else {
                                throw e;
                            }
                        }
                    }
                else {
                    throw new RuntimeException("FreeMarker bug: Bad key type");
                }
            else {
                for (i = 0; i < ln; i++) {
                    item = seq.get(i);
                    Object key = item;
                    for (int j = 0; j < keyCnt; j++) {
                        try {
                            key = ((TemplateHashModelkey).get(keys[j]);
                        catch (ClassCastException e) {
                            if (!(key instanceof TemplateHashModel)) {
                                throw new TemplateModelException(
                                        startErrorMessage(keys)
                                        "Problem with the sequence item at index " + i + ": "
                                        "Can't get the " + StringUtil.jQuote(keys[j])
                                        " subvariable, because the value is not a hash.");
                            else {
                                throw e;
                            }
                        }
                        if (key == null) {
                            throw new TemplateModelException(
                                    startErrorMessage(keys)
                                    "Problem with the sequence item at index " + i + ": "
                                    "The " + StringUtil.jQuote(keys[j])
                                    " subvariable was not found.");
                        }
                    }
                    if (keyType == KEY_TYPE_STRING) {
                        try {
                            res.add(new KVP(
                                    ((TemplateScalarModelkey).getAsString(),
                                    item));
                        catch (ClassCastException e) {
                            if (!(key instanceof TemplateScalarModel)) {
                                throw new TemplateModelException(
                                        startErrorMessage(keys)
                                        "All key values in the sequence must be "
                                        "date/time values, because the first key "
                                        "value was a date/time. The key value at "
                                        "index " + i + " is not a date/time.");
                            else {
                                throw e;
                            }
                        }
                    else if (keyType == KEY_TYPE_NUMBER) {
                        try {
                            res.add(new KVP(
                                    ((TemplateNumberModelkey).getAsNumber(),
                                    item));
                        catch (ClassCastException e) {
                            if (!(key instanceof TemplateNumberModel)) {
                                throw new TemplateModelException(
                                        startErrorMessage(keys)
                                        "All key values in the sequence must be "
                                        "numbers, because the first key "
                                        "value was a number. The key value at "
                                        "index " + i + " is not a number.");
                            }
                        }
                    else if (keyType == KEY_TYPE_DATE) {
                        try {
                            res.add(new KVP(
                                    ((TemplateDateModelkey).getAsDate(),
                                    item));
                        catch (ClassCastException e) {
                            if (!(key instanceof TemplateDateModel)) {
                                throw new TemplateModelException(
                                        startErrorMessage(keys)
                                        "All key values in the sequence must be "
                                        "dates, because the first key "
                                        "value was a date. The key value at "
                                        "index " + i + " is not a date.");
                            }
                        }
                    else {
                        throw new RuntimeException("FreeMarker bug: Bad key type");
                    }
                }
            }

            Comparator cmprtr;
            if (keyType == KEY_TYPE_STRING) {
                cmprtr = new LexicalKVPComparator(
                        Environment.getCurrentEnvironment().getCollator());
            else if (keyType == KEY_TYPE_NUMBER) {
                cmprtr = new NumericalKVPComparator(
                        Environment.getCurrentEnvironment()
                                .getArithmeticEngine());
            else if (keyType == KEY_TYPE_DATE) {
                cmprtr = new DateKVPComparator();
            else {
                throw new RuntimeException("FreeMarker bug: Bad key type");
            }

            try {
                Collections.sort(res, cmprtr);
            catch (ClassCastException exc) {
                throw new TemplateModelException(
                        startErrorMessage(keys)
                        "Unexpected error while sorting:" + exc, exc);
            }

            for (i = 0; i < ln; i++) {
                res.set(i, ((KVPres.get(i)).value);
            }

            return new TemplateModelListSequence(res);
        }

        private static class KVP {
            private KVP(Object key, Object value) {
                this.key = key;
                this.value = value;
            }

            private Object key;
            private Object value;
        }

        private static class NumericalKVPComparator implements Comparator {
            private ArithmeticEngine ae;

            private NumericalKVPComparator(ArithmeticEngine ae) {
                this.ae = ae;
            }

            public int compare(Object arg0, Object arg1) {
                try {
                    return ae.compareNumbers(
                            (Number) ((KVParg0).key,
                            (Number) ((KVParg1).key);
                catch (TemplateException e) {
                    throw new ClassCastException(
                        "Failed to compare numbers: " + e);
                }
            }
        }

        private static class LexicalKVPComparator implements Comparator {
            private Collator collator;

            LexicalKVPComparator(Collator collator) {
                this.collator = collator;
            }

            public int compare(Object arg0, Object arg1) {
                return collator.compare(
                        ((KVParg0).key, ((KVParg1).key);
            }
        }
        
        private static class DateKVPComparator implements Comparator, Serializable {

            public int compare(Object arg0, Object arg1) {
                return ((Date) ((KVParg0).key).compareTo(
                        (Date) ((KVParg1).key);
            }
        }
        
    }
    
    static class sort_byBI extends sortBI {
        TemplateModel calculateResult(TemplateSequenceModel seq)
        {
            return new BIMethod(seq);
        }
        
        static class BIMethod implements TemplateMethodModelEx {
            TemplateSequenceModel seq;
            
            BIMethod(TemplateSequenceModel seq) {
                this.seq = seq;
            }
            
            public Object exec(List params)
                    throws TemplateModelException {
                if (params.size() == 0) {
                    throw new TemplateModelException(
                            "?sort_by(key) needs exactly 1 argument.");
                }
                String[] subvars;
                Object obj = params.get(0);
                if (obj instanceof TemplateScalarModel) {
                    subvars = new String[]{((TemplateScalarModelobj).getAsString()};
                else if (obj instanceof TemplateSequenceModel) {
                    TemplateSequenceModel seq = (TemplateSequenceModelobj;
                    int ln = seq.size();
                    subvars = new String[ln];
                    for (int i = 0; i < ln; i++) {
                        Object item = seq.get(i);
                        try {
                            subvars[i((TemplateScalarModelitem)
                                    .getAsString();
                        catch (ClassCastException e) {
                            if (!(item instanceof TemplateScalarModel)) {
                                throw new TemplateModelException(
                                        "The argument to ?sort_by(key), when it "
                                        "is a sequence, must be a sequence of "
                                        "strings, but the item at index " + i
                                        " is not a string." );
                            }
                        }
                    }
                else {
                    throw new TemplateModelException(
                            "The argument to ?sort_by(key) must be a string "
                            "(the name of the subvariable), or a sequence of "
                            "strings (the \"path\" to the subvariable).");
                }
                return sort(seq, subvars)
            }
        }
    }

    static class seq_containsBI extends BuiltIn {
        TemplateModel _getAsTemplateModel(Environment env)
                throws TemplateException {
            TemplateModel model = target.getAsTemplateModel(env);
            if (model instanceof TemplateSequenceModel) {
                return new BIMethodForSequence((TemplateSequenceModelmodel, env);
            else if (model instanceof TemplateCollectionModel) {
                return new BIMethodForCollection((TemplateCollectionModelmodel, env);
            else {
                throw invalidTypeException(model, target, env, "sequence or collection");
            }
        }

        private static class BIMethodForSequence implements TemplateMethodModelEx {
            private TemplateSequenceModel m_seq;
            private Environment m_env;

            private BIMethodForSequence(TemplateSequenceModel seq, Environment env) {
                m_seq = seq;
                m_env = env;
            }

            public Object exec(List args)
                    throws TemplateModelException {
                if (args.size() != 1)
                    throw new TemplateModelException("?seq_contains(...) expects one argument.");
                TemplateModel arg = (TemplateModelargs.get(0);
                int size = m_seq.size();
                for (int i = 0; i < size; i++) {
                    if (modelsEqual(m_seq.get(i), arg, m_env))
                        return TemplateBooleanModel.TRUE;
                }
                return TemplateBooleanModel.FALSE;
            }

        }
    
        private static class BIMethodForCollection implements TemplateMethodModelEx {
            private TemplateCollectionModel m_coll;
            private Environment m_env;

            private BIMethodForCollection(TemplateCollectionModel coll, Environment env) {
                m_coll = coll;
                m_env = env;
            }

            public Object exec(List args)
                    throws TemplateModelException {
                if (args.size() != 1)
                    throw new TemplateModelException("?seq_contains(...) expects one argument.");
                TemplateModel arg = (TemplateModelargs.get(0);
                TemplateModelIterator it = m_coll.iterator();
                while (it.hasNext()) {
                    if (modelsEqual(it.next(), arg, m_env))
                        return TemplateBooleanModel.TRUE;
                }
                return TemplateBooleanModel.FALSE;
            }

        }
    
    }

    static class seq_index_ofBI extends BuiltIn {
        private int m_dir;

        public seq_index_ofBI(int dir) {
            m_dir = dir;
        }

        TemplateModel _getAsTemplateModel(Environment env)
                throws TemplateException {
            TemplateModel model = target.getAsTemplateModel(env);
            if (!(model instanceof TemplateSequenceModel))
                throw invalidTypeException(model, target, env, "sequence");
            return new BIMethod((TemplateSequenceModelmodel, env);
        }

        private class BIMethod implements TemplateMethodModelEx {
            private TemplateSequenceModel m_seq;
            private Environment m_env;

            private BIMethod(TemplateSequenceModel seq, Environment env) {
                m_seq = seq;
                m_env = env;
            }

            public Object exec(List args)
                    throws TemplateModelException {
                int argcnt = args.size();
                if (argcnt != && argcnt != 2) {
                    throw new TemplateModelException(
                            getBuiltinTemplate() " expects 1 or 2 arguments.");
                }
                
                int startIndex;
                int seqSize = m_seq.size();
                TemplateModel arg = (TemplateModelargs.get(0);
                if (argcnt > 1) {
                    Object obj = args.get(1);
                    if (!(obj instanceof TemplateNumberModel)) {
                        throw new TemplateModelException(
                                getBuiltinTemplate()
                                "expects a number as its second argument.");
                    }
                    startIndex = ((TemplateNumberModelobj).getAsNumber().intValue();
                    if (m_dir == 1) {
                        if (startIndex >= seqSize) {
                            return Constants.MINUS_ONE;
                        }
                        if (startIndex < 0) {
                            startIndex = 0;
                        }
                    else {
                        if (startIndex >= seqSize) {
                            startIndex = seqSize - 1;
                        }
                        if (startIndex < 0) {
                            return Constants.MINUS_ONE;
                        }
                    }
                else {
                    if (m_dir == 1) {
                        startIndex = 0;
                    else {
                        startIndex = seqSize - 1;
                    }
                }
                
                if (m_dir == 1) {
                    for (int i = startIndex; i < seqSize; i++) {
                        if (modelsEqual(m_seq.get(i), arg, m_env))
                            return new SimpleNumber(i);
                    }
                else {
                    for (int i = startIndex; i >= 0; i--) {
                        if (modelsEqual(m_seq.get(i), arg, m_env))
                            return new SimpleNumber(i);
                    }
                }
                return Constants.MINUS_ONE;
            }
            
            private String getBuiltinTemplate() {
                    if (m_dir == 1)
                        return "?seq_indexOf(...)";
                    else
                        return "?seq_lastIndexOf(...)";
            }
        }
    }

    static class chunkBI extends SequenceBuiltIn {

        TemplateModel calculateResult(TemplateSequenceModel tsmthrows TemplateModelException {
            return new BIMethod(tsm);
        }

        private static class BIMethod implements TemplateMethodModelEx {
            
            private final TemplateSequenceModel tsm;

            private BIMethod(TemplateSequenceModel tsm) {
                this.tsm = tsm;
            }

            public Object exec(List argsthrows TemplateModelException {
                int numArgs = args.size();
                if (numArgs != && numArgs !=2) {
                    throw new TemplateModelException(
                            "?chunk(...) expects 1 or 2 arguments.");
                }
                
                Object chunkSize = args.get(0);
                if (!(chunkSize instanceof TemplateNumberModel)) {
                    throw new TemplateModelException(
                            "?chunk(...) expects a number as "
                            "its 1st argument.");
                }
                
                return new ChunkedSequence(
                        tsm,
                        ((TemplateNumberModelchunkSize).getAsNumber().intValue(),
                        numArgs > (TemplateModelargs.get(1null);
            }
        }
        
        private static class ChunkedSequence implements TemplateSequenceModel {
            
            private final TemplateSequenceModel wrappedTsm;
            
            private final int chunkSize;
            
            private final TemplateModel fillerItem;
            
            private final int numberOfChunks;
            
            private ChunkedSequence(
                    TemplateSequenceModel wrappedTsm, int chunkSize, TemplateModel fillerItem)
                    throws TemplateModelException {
                if (chunkSize < 1) {
                    throw new TemplateModelException(
                            "The 1st argument to ?chunk(...) must be at least 1.");
                }
                this.wrappedTsm = wrappedTsm;
                this.chunkSize = chunkSize;
                this.fillerItem = fillerItem;
                numberOfChunks = (wrappedTsm.size() + chunkSize - 1/ chunkSize; 
            }

            public TemplateModel get(final int chunkIndex)
                    throws TemplateModelException {
                if (chunkIndex >= numberOfChunks) {
                    return null;
                }
                
                return new TemplateSequenceModel() {
                    
                    private final int baseIndex = chunkIndex * chunkSize;

                    public TemplateModel get(int relIndex)
                            throws TemplateModelException {
                        int absIndex = baseIndex + relIndex;
                        if (absIndex < wrappedTsm.size()) {
                            return wrappedTsm.get(absIndex);
                        else {
                            return absIndex < numberOfChunks * chunkSize
                                ? fillerItem
                                null;
                        }
                    }

                    public int size() throws TemplateModelException {
                        return fillerItem != null || chunkIndex + < numberOfChunks
                                ? chunkSize
                                : wrappedTsm.size() - baseIndex;
                    }
                    
                };
            }

            public int size() throws TemplateModelException {
                return numberOfChunks;
            }
            
        }
        
    }
    
    /*
     * WARNING! This algorithm is duplication of ComparisonExpression.isTrue(...).
     * Thus, if you update this method, then you have to update that too!
     */
    public static boolean modelsEqual(TemplateModel model1, TemplateModel model2,
                                Environment env)
            throws TemplateModelException {
        if (env.isClassicCompatible()) {
            if (model1 == null) {
                model1 = TemplateScalarModel.EMPTY_STRING;
            }
            if (model2 == null) {
                model2 = TemplateScalarModel.EMPTY_STRING;
            }
        }

        int comp = -1;
        if(model1 instanceof TemplateNumberModel && model2 instanceof TemplateNumberModel) {
            Number first = ((TemplateNumberModelmodel1).getAsNumber();
            Number second = ((TemplateNumberModelmodel2).getAsNumber();
            ArithmeticEngine ae = env.getArithmeticEngine();
            try {
                comp = ae.compareNumbers(first, second);
            catch (TemplateException ex) {
                throw new TemplateModelException(ex);
            }
        }
        else if(model1 instanceof TemplateDateModel && model2 instanceof TemplateDateModel) {
            TemplateDateModel ltdm = (TemplateDateModel)model1;
            TemplateDateModel rtdm = (TemplateDateModel)model2;
            int ltype = ltdm.getDateType();
            int rtype = rtdm.getDateType();
            if(ltype != rtype) {
                throw new TemplateModelException(
                    "Can not compare dates of different type. Left date is of "
                    + TemplateDateModel.TYPE_NAMES.get(ltype)
                    " type, right date is of "
                    + TemplateDateModel.TYPE_NAMES.get(rtype" type.");
            }
            if(ltype == TemplateDateModel.UNKNOWN) {
                throw new TemplateModelException(
                    "Left date is of UNKNOWN type, and can not be compared.");
            }
            if(rtype == TemplateDateModel.UNKNOWN) {
                throw new TemplateModelException(
                    "Right date is of UNKNOWN type, and can not be compared.");
            }
            Date first = ltdm.getAsDate();
            Date second = rtdm.getAsDate();
            comp = first.compareTo(second);
        }
        else if(model1 instanceof TemplateScalarModel && model2 instanceof TemplateScalarModel) {
            String first = ((TemplateScalarModelmodel1).getAsString();
            String second = ((TemplateScalarModelmodel2).getAsString();
            comp = env.getCollator().compare(first, second);
        }
        else if(model1 instanceof TemplateBooleanModel && model2 instanceof TemplateBooleanModel) {
            boolean first = ((TemplateBooleanModel)model1).getAsBoolean();
            boolean second = ((TemplateBooleanModel)model2).getAsBoolean();
            comp = (first ? 0(second ? 0);
        }

        return (comp == 0);
    }
    
}