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 }