    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *     http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.jxpath.ri.compiler;
    018
    019    import java.text.DecimalFormat;
    020    import java.text.DecimalFormatSymbols;
    021    import java.text.NumberFormat;
    022    import java.util.Collection;
    023    import java.util.Locale;
    024
    025    import org.apache.commons.jxpath.BasicNodeSet;
    026    import org.apache.commons.jxpath.JXPathContext;
    027    import org.apache.commons.jxpath.JXPathException;
    028    import org.apache.commons.jxpath.JXPathInvalidSyntaxException;
    029    import org.apache.commons.jxpath.NodeSet;
    030    import org.apache.commons.jxpath.ri.Compiler;
    031    import org.apache.commons.jxpath.ri.EvalContext;
    032    import org.apache.commons.jxpath.ri.InfoSetUtil;
    033    import org.apache.commons.jxpath.ri.axes.NodeSetContext;
    034    import org.apache.commons.jxpath.ri.model.NodePointer;
    035
    036    /**
    037     * An element of the compile tree representing one of built-in functions
    038     * like "position()" or "number()".
    039     *
    040     * @author Dmitri Plotnikov
    041     * @version $Revision: 668329 $ $Date: 2008-06-16 16:59:48 -0500 (Mon, 16 Jun 2008) $
    042     */
    043    public class CoreFunction extends Operation {
    044
    045        private static final Double ZERO = new Double(0);
    046        private int functionCode;
    047
    048        /**
    049         * Create a new CoreFunction.
    050         * @param functionCode int function code
    051         * @param args argument Expressions
    052         */
    053        public CoreFunction(int functionCode, Expression[] args) {
    054            super(args);
    055            this.functionCode = functionCode;
    056        }
    057
    058        /**
    059         * Get the function code.
    060         * @return int function code
    061         */
    062        public int getFunctionCode() {
    063            return functionCode;
    064        }
    065
    066        /**
    067         * Get the name of this function.
    068         * @return String function name
    069         */
    070        protected String getFunctionName() {
    071            switch (functionCode) {
    072                case Compiler.FUNCTION_LAST :
    073                    return "last";
    074                case Compiler.FUNCTION_POSITION :
    075                    return "position";
    076                case Compiler.FUNCTION_COUNT :
    077                    return "count";
    078                case Compiler.FUNCTION_ID :
    079                    return "id";
    080                case Compiler.FUNCTION_LOCAL_NAME :
    081                    return "local-name";
    082                case Compiler.FUNCTION_NAMESPACE_URI :
    083                    return "namespace-uri";
    084                case Compiler.FUNCTION_NAME :
    085                    return "name";
    086                case Compiler.FUNCTION_STRING :
    087                    return "string";
    088                case Compiler.FUNCTION_CONCAT :
    089                    return "concat";
    090                case Compiler.FUNCTION_STARTS_WITH :
    091                    return "starts-with";
    092                case Compiler.FUNCTION_CONTAINS :
    093                    return "contains";
    094                case Compiler.FUNCTION_SUBSTRING_BEFORE :
    095                    return "substring-before";
    096                case Compiler.FUNCTION_SUBSTRING_AFTER :
    097                    return "substring-after";
    098                case Compiler.FUNCTION_SUBSTRING :
    099                    return "substring";
    100                case Compiler.FUNCTION_STRING_LENGTH :
    101                    return "string-length";
    102                case Compiler.FUNCTION_NORMALIZE_SPACE :
    103                    return "normalize-space";
    104                case Compiler.FUNCTION_TRANSLATE :
    105                    return "translate";
    106                case Compiler.FUNCTION_BOOLEAN :
    107                    return "boolean";
    108                case Compiler.FUNCTION_NOT :
    109                    return "not";
    110                case Compiler.FUNCTION_TRUE :
    111                    return "true";
    112                case Compiler.FUNCTION_FALSE :
    113                    return "false";
    114                case Compiler.FUNCTION_LANG :
    115                    return "lang";
    116                case Compiler.FUNCTION_NUMBER :
    117                    return "number";
    118                case Compiler.FUNCTION_SUM :
    119                    return "sum";
    120                case Compiler.FUNCTION_FLOOR :
    121                    return "floor";
    122                case Compiler.FUNCTION_CEILING :
    123                    return "ceiling";
    124                case Compiler.FUNCTION_ROUND :
    125                    return "round";
    126                case Compiler.FUNCTION_KEY :
    127                    return "key";
    128                case Compiler.FUNCTION_FORMAT_NUMBER:
    129                    return "format-number";
    130                default:
    131                    return "unknownFunction" + functionCode + "()";
    132            }
    133        }
    134
    135        /**
    136         * Convenience method to return the first argument.
    137         * @return Expression
    138         */
    139        public Expression getArg1() {
    140            return args[0];
    141        }
    142
    143        /**
    144         * Convenience method to return the second argument.
    145         * @return Expression
    146         */
    147        public Expression getArg2() {
    148            return args[1];
    149        }
    150
    151        /**
    152         * Convenience method to return the third argument.
    153         * @return Expression
    154         */
    155        public Expression getArg3() {
    156            return args[2];
    157        }
    158
    159        /**
    160         * Return the number of argument Expressions.
    161         * @return int count
    162         */
    163        public int getArgumentCount() {
    164            if (args == null) {
    165                return 0;
    166            }
    167            return args.length;
    168        }
    169
    170        /**
    171         * Returns true if any argument is context dependent or if
    172         * the function is last(), position(), boolean(), local-name(),
    173         * name(), string(), lang(), number().
    174         * @return boolean
    175         */
    176        public boolean computeContextDependent() {
    177            if (super.computeContextDependent()) {
    178                return true;
    179            }
    180
    181            switch (functionCode) {
    182                case Compiler.FUNCTION_LAST:
    183                case Compiler.FUNCTION_POSITION:
    184                    return true;
    185
    186                case Compiler.FUNCTION_BOOLEAN:
    187                case Compiler.FUNCTION_LOCAL_NAME:
    188                case Compiler.FUNCTION_NAME:
    189                case Compiler.FUNCTION_NAMESPACE_URI:
    190                case Compiler.FUNCTION_STRING:
    191                case Compiler.FUNCTION_LANG:
    192                case Compiler.FUNCTION_NUMBER:
    193                    return args == null || args.length == 0;
    194
    195                case Compiler.FUNCTION_FORMAT_NUMBER:
    196                    return args != null && args.length == 2;
    197
    198                case Compiler.FUNCTION_COUNT:
    199                case Compiler.FUNCTION_ID:
    200                case Compiler.FUNCTION_CONCAT:
    201                case Compiler.FUNCTION_STARTS_WITH:
    202                case Compiler.FUNCTION_CONTAINS:
    203                case Compiler.FUNCTION_SUBSTRING_BEFORE:
    204                case Compiler.FUNCTION_SUBSTRING_AFTER:
    205                case Compiler.FUNCTION_SUBSTRING:
    206                case Compiler.FUNCTION_STRING_LENGTH:
    207                case Compiler.FUNCTION_NORMALIZE_SPACE:
    208                case Compiler.FUNCTION_TRANSLATE:
    209                case Compiler.FUNCTION_NOT:
    210                case Compiler.FUNCTION_TRUE:
    211                case Compiler.FUNCTION_FALSE:
    212                case Compiler.FUNCTION_SUM:
    213                case Compiler.FUNCTION_FLOOR:
    214                case Compiler.FUNCTION_CEILING:
    215                case Compiler.FUNCTION_ROUND:
    216                default:
    217                    return false;
    218            }
    219        }
    220
    221        public String toString() {
    222            StringBuffer buffer = new StringBuffer();
    223            buffer.append(getFunctionName());
    224            buffer.append('(');
    225            Expression[] args = getArguments();
    226            if (args != null) {
    227                for (int i = 0; i < args.length; i++) {
    228                    if (i > 0) {
    229                        buffer.append(", ");
    230                    }
    231                    buffer.append(args[i]);
    232                }
    233            }
    234            buffer.append(')');
    235            return buffer.toString();
    236        }
    237
    238        public Object compute(EvalContext context) {
    239            return computeValue(context);
    240        }
    241
    242        public Object computeValue(EvalContext context) {
    243            switch (functionCode) {
    244                case Compiler.FUNCTION_LAST :
    245                    return functionLast(context);
    246                case Compiler.FUNCTION_POSITION :
    247                    return functionPosition(context);
    248                case Compiler.FUNCTION_COUNT :
    249                    return functionCount(context);
    250                case Compiler.FUNCTION_LANG :
    251                    return functionLang(context);
    252                case Compiler.FUNCTION_ID :
    253                    return functionID(context);
    254                case Compiler.FUNCTION_LOCAL_NAME :
    255                    return functionLocalName(context);
    256                case Compiler.FUNCTION_NAMESPACE_URI :
    257                    return functionNamespaceURI(context);
    258                case Compiler.FUNCTION_NAME :
    259                    return functionName(context);
    260                case Compiler.FUNCTION_STRING :
    261                    return functionString(context);
    262                case Compiler.FUNCTION_CONCAT :
    263                    return functionConcat(context);
    264                case Compiler.FUNCTION_STARTS_WITH :
    265                    return functionStartsWith(context);
    266                case Compiler.FUNCTION_CONTAINS :
    267                    return functionContains(context);
    268                case Compiler.FUNCTION_SUBSTRING_BEFORE :
    269                    return functionSubstringBefore(context);
    270                case Compiler.FUNCTION_SUBSTRING_AFTER :
    271                    return functionSubstringAfter(context);
    272                case Compiler.FUNCTION_SUBSTRING :
    273                    return functionSubstring(context);
    274                case Compiler.FUNCTION_STRING_LENGTH :
    275                    return functionStringLength(context);
    276                case Compiler.FUNCTION_NORMALIZE_SPACE :
    277                    return functionNormalizeSpace(context);
    278                case Compiler.FUNCTION_TRANSLATE :
    279                    return functionTranslate(context);
    280                case Compiler.FUNCTION_BOOLEAN :
    281                    return functionBoolean(context);
    282                case Compiler.FUNCTION_NOT :
    283                    return functionNot(context);
    284                case Compiler.FUNCTION_TRUE :
    285                    return functionTrue(context);
    286                case Compiler.FUNCTION_FALSE :
    287                    return functionFalse(context);
    288                case Compiler.FUNCTION_NULL :
    289                    return functionNull(context);
    290                case Compiler.FUNCTION_NUMBER :
    291                    return functionNumber(context);
    292                case Compiler.FUNCTION_SUM :
    293                    return functionSum(context);
    294                case Compiler.FUNCTION_FLOOR :
    295                    return functionFloor(context);
    296                case Compiler.FUNCTION_CEILING :
    297                    return functionCeiling(context);
    298                case Compiler.FUNCTION_ROUND :
    299                    return functionRound(context);
    300                case Compiler.FUNCTION_KEY :
    301                    return functionKey(context);
    302                case Compiler.FUNCTION_FORMAT_NUMBER :
    303                    return functionFormatNumber(context);
    304                default:
    305                    return null;
    306            }
    307        }
    308
    309        /**
    310         * last() implementation.
    311         * @param context evaluation context
    312         * @return Number
    313         */
    314        protected Object functionLast(EvalContext context) {
    315            assertArgCount(0);
    316            // Move the position to the beginning and iterate through
    317            // the context to count nodes.
    318            int old = context.getCurrentPosition();
    319            context.reset();
    320            int count = 0;
    321            while (context.nextNode()) {
    322                count++;
    323            }
    324
    325            // Restore the current position.
    326            if (old != 0) {
    327                context.setPosition(old);
    328            }
    329            return new Double(count);
    330        }
    331
    332        /**
    333         * position() implementation.
    334         * @param context evaluation context
    335         * @return Number
    336         */
    337        protected Object functionPosition(EvalContext context) {
    338            assertArgCount(0);
    339            return new Integer(context.getCurrentPosition());
    340        }
    341
    342        /**
    343         * count() implementation.
    344         * @param context evaluation context
    345         * @return Number
    346         */
    347        protected Object functionCount(EvalContext context) {
    348            assertArgCount(1);
    349            Expression arg1 = getArg1();
    350            int count = 0;
    351            Object value = arg1.compute(context);
    352            if (value instanceof NodePointer) {
    353                value = ((NodePointer) value).getValue();
    354            }
    355            if (value instanceof EvalContext) {
    356                EvalContext ctx = (EvalContext) value;
    357                while (ctx.hasNext()) {
    358                    ctx.next();
    359                    count++;
    360                }
    361            }
    362            else if (value instanceof Collection) {
    363                count = ((Collection) value).size();
    364            }
    365            else if (value == null) {
    366                count = 0;
    367            }
    368            else {
    369                count = 1;
    370            }
    371            return new Double(count);
    372        }
    373
    374        /**
    375         * lang() implementation.
    376         * @param context evaluation context
    377         * @return Boolean
    378         */
    379        protected Object functionLang(EvalContext context) {
    380            assertArgCount(1);
    381            String lang = InfoSetUtil.stringValue(getArg1().computeValue(context));
    382            NodePointer pointer = (NodePointer) context.getSingleNodePointer();
    383            if (pointer == null) {
    384                return Boolean.FALSE;
    385            }
    386            return pointer.isLanguage(lang) ? Boolean.TRUE : Boolean.FALSE;
    387        }
    388
    389        /**
    390         * id() implementation.
    391         * @param context evaluation context
    392         * @return Pointer
    393         */
    394        protected Object functionID(EvalContext context) {
    395            assertArgCount(1);
    396            String id = InfoSetUtil.stringValue(getArg1().computeValue(context));
    397            JXPathContext jxpathContext = context.getJXPathContext();
    398            NodePointer pointer = (NodePointer) jxpathContext.getContextPointer();
    399            return pointer.getPointerByID(jxpathContext, id);
    400        }
    401
    402        /**
    403         * key() implementation.
    404         * @param context evaluation context
    405         * @return various Object
    406         */
    407        protected Object functionKey(EvalContext context) {
    408            assertArgCount(2);
    409            String key = InfoSetUtil.stringValue(getArg1().computeValue(context));
    410            Object value = getArg2().compute(context);
    411            EvalContext ec = null;
    412            if (value instanceof EvalContext) {
    413                ec = (EvalContext) value;
    414                if (ec.hasNext()) {
    415                    value = ((NodePointer) ec.next()).getValue();
    416                }
    417                else { // empty context -> empty results
    418                    return new NodeSetContext(context, new BasicNodeSet());
    419                }
    420            }
    421            JXPathContext jxpathContext = context.getJXPathContext();
    422            NodeSet nodeSet = jxpathContext.getNodeSetByKey(key, value);
    423            if (ec != null && ec.hasNext()) {
    424                BasicNodeSet accum = new BasicNodeSet();
    425                accum.add(nodeSet);
    426                while (ec.hasNext()) {
    427                    value = ((NodePointer) ec.next()).getValue();
    428                    accum.add(jxpathContext.getNodeSetByKey(key, value));
    429                }
    430                nodeSet = accum;
    431            }
    432            return new NodeSetContext(context, nodeSet);
    433        }
    434
    435        /**
    436         * namespace-uri() implementation.
    437         * @param context evaluation context
    438         * @return String
    439         */
    440        protected Object functionNamespaceURI(EvalContext context) {
    441            if (getArgumentCount() == 0) {
    442                NodePointer ptr = context.getCurrentNodePointer();
    443                String str = ptr.getNamespaceURI();
    444                return str == null ? "" : str;
    445            }
    446            assertArgCount(1);
    447            Object set = getArg1().compute(context);
    448            if (set instanceof EvalContext) {
    449                EvalContext ctx = (EvalContext) set;
    450                if (ctx.hasNext()) {
    451                    NodePointer ptr = (NodePointer) ctx.next();
    452                    String str = ptr.getNamespaceURI();
    453                    return str == null ? "" : str;
    454                }
    455            }
    456            return "";
    457        }
    458
    459        /**
    460         * local-name() implementation.
    461         * @param context evaluation context
    462         * @return String
    463         */
    464        protected Object functionLocalName(EvalContext context) {
    465            if (getArgumentCount() == 0) {
    466                NodePointer ptr = context.getCurrentNodePointer();
    467                return ptr.getName().getName();
    468            }
    469            assertArgCount(1);
    470            Object set = getArg1().compute(context);
    471            if (set instanceof EvalContext) {
    472                EvalContext ctx = (EvalContext) set;
    473                if (ctx.hasNext()) {
    474                    NodePointer ptr = (NodePointer) ctx.next();
    475                    return ptr.getName().getName();
    476                }
    477            }
    478            return "";
    479        }
    480
    481        /**
    482         * name() implementation.
    483         * @param context evaluation context
    484         * @return String
    485         */
    486        protected Object functionName(EvalContext context) {
    487            if (getArgumentCount() == 0) {
    488                NodePointer ptr = context.getCurrentNodePointer();
    489                return ptr.getName().toString();
    490            }
    491            assertArgCount(1);
    492            Object set = getArg1().compute(context);
    493            if (set instanceof EvalContext) {
    494                EvalContext ctx = (EvalContext) set;
    495                if (ctx.hasNext()) {
    496                    NodePointer ptr = (NodePointer) ctx.next();
    497                    return ptr.getName().toString();
    498                }
    499            }
    500            return "";
    501        }
    502
    503        /**
    504         * string() implementation.
    505         * @param context evaluation context
    506         * @return String
    507         */
    508        protected Object functionString(EvalContext context) {
    509            if (getArgumentCount() == 0) {
    510                return InfoSetUtil.stringValue(context.getCurrentNodePointer());
    511            }
    512            assertArgCount(1);
    513            return InfoSetUtil.stringValue(getArg1().computeValue(context));
    514        }
    515
    516        /**
    517         * concat() implementation.
    518         * @param context evaluation context
    519         * @return String
    520         */
    521        protected Object functionConcat(EvalContext context) {
    522            if (getArgumentCount() < 2) {
    523                assertArgCount(2);
    524            }
    525            StringBuffer buffer = new StringBuffer();
    526            Expression[] args = getArguments();
    527            for (int i = 0; i < args.length; i++) {
    528                buffer.append(InfoSetUtil.stringValue(args[i].compute(context)));
    529            }
    530            return buffer.toString();
    531        }
    532
    533        /**
    534         * starts-with() implementation.
    535         * @param context evaluation context
    536         * @return Boolean
    537         */
    538        protected Object functionStartsWith(EvalContext context) {
    539            assertArgCount(2);
    540            String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
    541            String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
    542            return s1.startsWith(s2) ? Boolean.TRUE : Boolean.FALSE;
    543        }
    544
    545        /**
    546         * contains() implementation.
    547         * @param context evaluation context
    548         * @return Boolean
    549         */
    550        protected Object functionContains(EvalContext context) {
    551            assertArgCount(2);
    552            String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
    553            String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
    554            return s1.indexOf(s2) != -1 ? Boolean.TRUE : Boolean.FALSE;
    555        }
    556
    557        /**
    558         * substring-before() implementation.
    559         * @param context evaluation context
    560         * @return String
    561         */
    562        protected Object functionSubstringBefore(EvalContext context) {
    563            assertArgCount(2);
    564            String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
    565            String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
    566            int index = s1.indexOf(s2);
    567            if (index == -1) {
    568                return "";
    569            }
    570            return s1.substring(0, index);
    571        }
    572
    573        /**
    574         * substring-after() implementation.
    575         * @param context evaluation context
    576         * @return String
    577         */
    578        protected Object functionSubstringAfter(EvalContext context) {
    579            assertArgCount(2);
    580            String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
    581            String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
    582            int index = s1.indexOf(s2);
    583            if (index == -1) {
    584                return "";
    585            }
    586            return s1.substring(index + s2.length());
    587        }
    588
    589        /**
    590         * substring() implementation.
    591         * @param context evaluation context
    592         * @return String
    593         */
    594        protected Object functionSubstring(EvalContext context) {
    595            final int minArgs = 2;
    596            final int maxArgs = 3;
    597            assertArgRange(minArgs, maxArgs);
    598            int ac = getArgumentCount();
    599
    600            String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
    601            double from = InfoSetUtil.doubleValue(getArg2().computeValue(context));
    602            if (Double.isNaN(from)) {
    603                return "";
    604            }
    605
    606            from = Math.round(from);
    607            if (from > s1.length() + 1) {
    608                return "";
    609            }
    610            if (ac == 2) {
    611                if (from < 1) {
    612                    from = 1;
    613                }
    614                return s1.substring((int) from - 1);
    615            }
    616            double length =
    617                InfoSetUtil.doubleValue(getArg3().computeValue(context));
    618            length = Math.round(length);
    619            if (length < 0) {
    620                return "";
    621            }
    622
    623            double to = from + length;
    624            if (to < 1) {
    625                return "";
    626            }
    627
    628            if (to > s1.length() + 1) {
    629                if (from < 1) {
    630                    from = 1;
    631                }
    632                return s1.substring((int) from - 1);
    633            }
    634
    635            if (from < 1) {
    636                from = 1;
    637            }
    638            return s1.substring((int) from - 1, (int) (to - 1));
    639        }
    640
    641        /**
    642         * string-length() implementation.
    643         * @param context evaluation context
    644         * @return Number
    645         */
    646        protected Object functionStringLength(EvalContext context) {
    647            String s;
    648            if (getArgumentCount() == 0) {
    649                s = InfoSetUtil.stringValue(context.getCurrentNodePointer());
    650            }
    651            else {
    652                assertArgCount(1);
    653                s = InfoSetUtil.stringValue(getArg1().computeValue(context));
    654            }
    655            return new Double(s.length());
    656        }
    657
    658        /**
    659         * normalize-space() implementation.
    660         * @param context evaluation context
    661         * @return String
    662         */
    663        protected Object functionNormalizeSpace(EvalContext context) {
    664            assertArgCount(1);
    665            String s = InfoSetUtil.stringValue(getArg1().computeValue(context));
    666            char[] chars = s.toCharArray();
    667            int out = 0;
    668            int phase = 0;
    669            for (int in = 0; in < chars.length; in++) {
    670                switch (chars[in]) {
    671                    case ' ':
    672                    case '\t':
    673                    case '\r':
    674                    case '\n':
    675                        if (phase == 1) { // non-space
    676                            phase = 2;
    677                            chars[out++] = ' ';
    678                        }
    679                        break;
    680                    default:
    681                        chars[out++] = chars[in];
    682                        phase = 1;
    683                }
    684            }
    685            if (phase == 2) { // trailing-space
    686                out--;
    687            }
    688            return new String(chars, 0, out);
    689        }
    690
    691        /**
    692         * translate() implementation.
    693         * @param context evaluation context
    694         * @return String
    695         */
    696        protected Object functionTranslate(EvalContext context) {
    697            final int argCount = 3;
    698            assertArgCount(argCount);
    699            String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
    700            String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
    701            String s3 = InfoSetUtil.stringValue(getArg3().computeValue(context));
    702            char[] chars = s1.toCharArray();
    703            int out = 0;
    704            for (int in = 0; in < chars.length; in++) {
    705                char c = chars[in];
    706                int inx = s2.indexOf(c);
    707                if (inx != -1) {
    708                    if (inx < s3.length()) {
    709                        chars[out++] = s3.charAt(inx);
    710                    }
    711                }
    712                else {
    713                    chars[out++] = c;
    714                }
    715            }
    716            return new String(chars, 0, out);
    717        }
    718
    719        /**
    720         * boolean() implementation.
    721         * @param context evaluation context
    722         * @return Boolean
    723         */
    724        protected Object functionBoolean(EvalContext context) {
    725            assertArgCount(1);
    726            return InfoSetUtil.booleanValue(getArg1().computeValue(context))
    727                ? Boolean.TRUE
    728                : Boolean.FALSE;
    729        }
    730
    731        /**
    732         * not() implementation.
    733         * @param context evaluation context
    734         * @return Boolean
    735         */
    736        protected Object functionNot(EvalContext context) {
    737            assertArgCount(1);
    738            return InfoSetUtil.booleanValue(getArg1().computeValue(context))
    739                ? Boolean.FALSE
    740                : Boolean.TRUE;
    741        }
    742
    743        /**
    744         * true() implementation.
    745         * @param context evaluation context
    746         * @return Boolean.TRUE
    747         */
    748        protected Object functionTrue(EvalContext context) {
    749            assertArgCount(0);
    750            return Boolean.TRUE;
    751        }
    752
    753        /**
    754         * false() implementation.
    755         * @param context evaluation context
    756         * @return Boolean.FALSE
    757         */
    758        protected Object functionFalse(EvalContext context) {
    759            assertArgCount(0);
    760            return Boolean.FALSE;
    761        }
    762
    763        /**
    764         * null() implementation.
    765         * @param context evaluation context
    766         * @return null
    767         */
    768        protected Object functionNull(EvalContext context) {
    769            assertArgCount(0);
    770            return null;
    771        }
    772
    773        /**
    774         * number() implementation.
    775         * @param context evaluation context
    776         * @return Number
    777         */
    778        protected Object functionNumber(EvalContext context) {
    779            if (getArgumentCount() == 0) {
    780                return InfoSetUtil.number(context.getCurrentNodePointer());
    781            }
    782            assertArgCount(1);
    783            return InfoSetUtil.number(getArg1().computeValue(context));
    784        }
    785
    786        /**
    787         * sum() implementation.
    788         * @param context evaluation context
    789         * @return Number
    790         */
    791        protected Object functionSum(EvalContext context) {
    792            assertArgCount(1);
    793            Object v = getArg1().compute(context);
    794            if (v == null) {
    795                return ZERO;
    796            }
    797            if (v instanceof EvalContext) {
    798                double sum = 0.0;
    799                EvalContext ctx = (EvalContext) v;
    800                while (ctx.hasNext()) {
    801                    NodePointer ptr = (NodePointer) ctx.next();
    802                    sum += InfoSetUtil.doubleValue(ptr);
    803                }
    804                return new Double(sum);
    805            }
    806            throw new JXPathException(
    807                "Invalid argument type for 'sum': " + v.getClass().getName());
    808        }
    809
    810        /**
    811         * floor() implementation.
    812         * @param context evaluation context
    813         * @return Number
    814         */
    815        protected Object functionFloor(EvalContext context) {
    816            assertArgCount(1);
    817            double v = InfoSetUtil.doubleValue(getArg1().computeValue(context));
    818            if (Double.isNaN(v) || Double.isInfinite(v)) {
    819                return new Double(v);
    820            }
    821            return new Double(Math.floor(v));
    822        }
    823
    824        /**
    825         * ceiling() implementation.
    826         * @param context evaluation context
    827         * @return Number
    828         */
    829        protected Object functionCeiling(EvalContext context) {
    830            assertArgCount(1);
    831            double v = InfoSetUtil.doubleValue(getArg1().computeValue(context));
    832            if (Double.isNaN(v) || Double.isInfinite(v)) {
    833                return new Double(v);
    834            }
    835            return new Double(Math.ceil(v));
    836        }
    837
    838        /**
    839         * round() implementation.
    840         * @param context evaluation context
    841         * @return Number
    842         */
    843        protected Object functionRound(EvalContext context) {
    844            assertArgCount(1);
    845            double v = InfoSetUtil.doubleValue(getArg1().computeValue(context));
    846            if (Double.isNaN(v) || Double.isInfinite(v)) {
    847                return new Double(v);
    848            }
    849            return new Double(Math.round(v));
    850        }
    851
    852        /**
    853         * format-number() implementation.
    854         * @param context evaluation context
    855         * @return String
    856         */
    857        private Object functionFormatNumber(EvalContext context) {
    858            final int minArgs = 2;
    859            final int maxArgs = 3;
    860            assertArgRange(minArgs, maxArgs);
    861
    862            double number =
    863                InfoSetUtil.doubleValue(getArg1().computeValue(context));
    864            String pattern =
    865                InfoSetUtil.stringValue(getArg2().computeValue(context));
    866
    867            DecimalFormatSymbols symbols = null;
    868            if (getArgumentCount() == maxArgs) {
    869                String symbolsName =
    870                    InfoSetUtil.stringValue(getArg3().computeValue(context));
    871                symbols =
    872                    context.getJXPathContext().getDecimalFormatSymbols(symbolsName);
    873            }
    874            else {
    875                NodePointer pointer = context.getCurrentNodePointer();
    876                Locale locale;
    877                if (pointer != null) {
    878                    locale = pointer.getLocale();
    879                }
    880                else {
    881                    locale = context.getJXPathContext().getLocale();
    882                }
    883                symbols = new DecimalFormatSymbols(locale);
    884            }
    885
    886            DecimalFormat format = (DecimalFormat) NumberFormat.getInstance();
    887            format.setDecimalFormatSymbols(symbols);
    888            format.applyLocalizedPattern(pattern);
    889            return format.format(number);
    890        }
    891
    892        /**
    893         * Assert <code>count</code> args.
    894         * @param count int
    895         */
    896        private void assertArgCount(int count) {
    897            assertArgRange(count, count);
    898        }
    899
    900        /**
    901         * Assert at least <code>min</code>/at most <code>max</code> args.
    902         * @param min int
    903         * @param max int
    904         */
    905        private void assertArgRange(int min, int max) {
    906            int ct = getArgumentCount();
    907            if (ct < min || ct > max) {
    908                throw new JXPathInvalidSyntaxException(
    909                        "Incorrect number of arguments: " + this);
    910            }
    911        }
    912    }