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