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 }