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 */ 017package org.apache.commons.jxpath.ri.compiler; 018 019import java.text.DecimalFormat; 020import java.text.DecimalFormatSymbols; 021import java.text.NumberFormat; 022import java.util.Collection; 023import java.util.Locale; 024 025import org.apache.commons.jxpath.BasicNodeSet; 026import org.apache.commons.jxpath.JXPathContext; 027import org.apache.commons.jxpath.JXPathException; 028import org.apache.commons.jxpath.JXPathInvalidSyntaxException; 029import org.apache.commons.jxpath.NodeSet; 030import org.apache.commons.jxpath.ri.Compiler; 031import org.apache.commons.jxpath.ri.EvalContext; 032import org.apache.commons.jxpath.ri.InfoSetUtil; 033import org.apache.commons.jxpath.ri.axes.NodeSetContext; 034import 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: 779915 $ $Date: 2009-05-29 12:23:40 +0200 (Fr, 29 Mai 2009) $ 042 */ 043public 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_ENDS_WITH : 093 return "ends-with"; 094 case Compiler.FUNCTION_CONTAINS : 095 return "contains"; 096 case Compiler.FUNCTION_SUBSTRING_BEFORE : 097 return "substring-before"; 098 case Compiler.FUNCTION_SUBSTRING_AFTER : 099 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}