View Javadoc

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