1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.jxpath.ri.compiler;
17
18 import java.text.DecimalFormat;
19 import java.text.DecimalFormatSymbols;
20 import java.text.NumberFormat;
21 import java.util.Collection;
22 import java.util.Locale;
23
24 import org.apache.commons.jxpath.JXPathContext;
25 import org.apache.commons.jxpath.JXPathException;
26 import org.apache.commons.jxpath.ri.Compiler;
27 import org.apache.commons.jxpath.ri.EvalContext;
28 import org.apache.commons.jxpath.ri.InfoSetUtil;
29 import org.apache.commons.jxpath.ri.model.NodePointer;
30
31 /***
32 * An element of the compile tree representing one of built-in functions
33 * like "position()" or "number()".
34 *
35 * @author Dmitri Plotnikov
36 * @version $Revision: 1.16 $ $Date: 2004/04/01 02:53:45 $
37 */
38 public class CoreFunction extends Operation {
39
40 private static final Double ZERO = new Double(0);
41 private int functionCode;
42
43 public CoreFunction(int functionCode, Expression args[]) {
44 super(args);
45 this.functionCode = functionCode;
46 }
47
48 public int getFunctionCode() {
49 return functionCode;
50 }
51
52 protected String getFunctionName() {
53 switch (functionCode) {
54 case Compiler.FUNCTION_LAST :
55 return "last";
56 case Compiler.FUNCTION_POSITION :
57 return "position";
58 case Compiler.FUNCTION_COUNT :
59 return "count";
60 case Compiler.FUNCTION_ID :
61 return "id";
62 case Compiler.FUNCTION_LOCAL_NAME :
63 return "local-name";
64 case Compiler.FUNCTION_NAMESPACE_URI :
65 return "namespace-uri";
66 case Compiler.FUNCTION_NAME :
67 return "name";
68 case Compiler.FUNCTION_STRING :
69 return "string";
70 case Compiler.FUNCTION_CONCAT :
71 return "concat";
72 case Compiler.FUNCTION_STARTS_WITH :
73 return "starts-with";
74 case Compiler.FUNCTION_CONTAINS :
75 return "contains";
76 case Compiler.FUNCTION_SUBSTRING_BEFORE :
77 return "substring-before";
78 case Compiler.FUNCTION_SUBSTRING_AFTER :
79 return "substring-after";
80 case Compiler.FUNCTION_SUBSTRING :
81 return "substring";
82 case Compiler.FUNCTION_STRING_LENGTH :
83 return "string-length";
84 case Compiler.FUNCTION_NORMALIZE_SPACE :
85 return "normalize-space";
86 case Compiler.FUNCTION_TRANSLATE :
87 return "translate";
88 case Compiler.FUNCTION_BOOLEAN :
89 return "boolean";
90 case Compiler.FUNCTION_NOT :
91 return "not";
92 case Compiler.FUNCTION_TRUE :
93 return "true";
94 case Compiler.FUNCTION_FALSE :
95 return "false";
96 case Compiler.FUNCTION_LANG :
97 return "lang";
98 case Compiler.FUNCTION_NUMBER :
99 return "number";
100 case Compiler.FUNCTION_SUM :
101 return "sum";
102 case Compiler.FUNCTION_FLOOR :
103 return "floor";
104 case Compiler.FUNCTION_CEILING :
105 return "ceiling";
106 case Compiler.FUNCTION_ROUND :
107 return "round";
108 case Compiler.FUNCTION_KEY :
109 return "key";
110 case Compiler.FUNCTION_FORMAT_NUMBER:
111 return "format-number";
112 }
113 return "unknownFunction" + functionCode + "()";
114 }
115
116 public Expression getArg1() {
117 return args[0];
118 }
119
120 public Expression getArg2() {
121 return args[1];
122 }
123
124 public Expression getArg3() {
125 return args[2];
126 }
127
128 public int getArgumentCount() {
129 if (args == null) {
130 return 0;
131 }
132 return args.length;
133 }
134
135 /***
136 * Returns true if any argument is context dependent or if
137 * the function is last(), position(), boolean(), local-name(),
138 * name(), string(), lang(), number().
139 */
140 public boolean computeContextDependent() {
141 if (super.computeContextDependent()) {
142 return true;
143 }
144
145 switch(functionCode) {
146 case Compiler.FUNCTION_LAST:
147 case Compiler.FUNCTION_POSITION:
148 return true;
149
150 case Compiler.FUNCTION_BOOLEAN:
151 case Compiler.FUNCTION_LOCAL_NAME:
152 case Compiler.FUNCTION_NAME:
153 case Compiler.FUNCTION_NAMESPACE_URI:
154 case Compiler.FUNCTION_STRING:
155 case Compiler.FUNCTION_LANG:
156 case Compiler.FUNCTION_NUMBER:
157 return args == null || args.length == 0;
158
159 case Compiler.FUNCTION_COUNT:
160 case Compiler.FUNCTION_ID:
161 case Compiler.FUNCTION_CONCAT:
162 case Compiler.FUNCTION_STARTS_WITH:
163 case Compiler.FUNCTION_CONTAINS:
164 case Compiler.FUNCTION_SUBSTRING_BEFORE:
165 case Compiler.FUNCTION_SUBSTRING_AFTER:
166 case Compiler.FUNCTION_SUBSTRING:
167 case Compiler.FUNCTION_STRING_LENGTH:
168 case Compiler.FUNCTION_NORMALIZE_SPACE:
169 case Compiler.FUNCTION_TRANSLATE:
170 case Compiler.FUNCTION_NOT:
171 case Compiler.FUNCTION_TRUE:
172 case Compiler.FUNCTION_FALSE:
173 case Compiler.FUNCTION_SUM:
174 case Compiler.FUNCTION_FLOOR:
175 case Compiler.FUNCTION_CEILING:
176 case Compiler.FUNCTION_ROUND:
177 return false;
178
179 case Compiler.FUNCTION_FORMAT_NUMBER:
180 return args != null && args.length == 2;
181 }
182
183 return false;
184 }
185
186 public String toString() {
187 StringBuffer buffer = new StringBuffer();
188 buffer.append(getFunctionName());
189 buffer.append('(');
190 Expression args[] = getArguments();
191 if (args != null) {
192 for (int i = 0; i < args.length; i++) {
193 if (i > 0) {
194 buffer.append(", ");
195 }
196 buffer.append(args[i]);
197 }
198 }
199 buffer.append(')');
200 return buffer.toString();
201 }
202
203 public Object compute(EvalContext context) {
204 return computeValue(context);
205 }
206
207 /***
208 * Computes a built-in function
209 */
210 public Object computeValue(EvalContext context) {
211 switch (functionCode) {
212 case Compiler.FUNCTION_LAST :
213 return functionLast(context);
214 case Compiler.FUNCTION_POSITION :
215 return functionPosition(context);
216 case Compiler.FUNCTION_COUNT :
217 return functionCount(context);
218 case Compiler.FUNCTION_LANG :
219 return functionLang(context);
220 case Compiler.FUNCTION_ID :
221 return functionID(context);
222 case Compiler.FUNCTION_LOCAL_NAME :
223 return functionLocalName(context);
224 case Compiler.FUNCTION_NAMESPACE_URI :
225 return functionNamespaceURI(context);
226 case Compiler.FUNCTION_NAME :
227 return functionName(context);
228 case Compiler.FUNCTION_STRING :
229 return functionString(context);
230 case Compiler.FUNCTION_CONCAT :
231 return functionConcat(context);
232 case Compiler.FUNCTION_STARTS_WITH :
233 return functionStartsWith(context);
234 case Compiler.FUNCTION_CONTAINS :
235 return functionContains(context);
236 case Compiler.FUNCTION_SUBSTRING_BEFORE :
237 return functionSubstringBefore(context);
238 case Compiler.FUNCTION_SUBSTRING_AFTER :
239 return functionSubstringAfter(context);
240 case Compiler.FUNCTION_SUBSTRING :
241 return functionSubstring(context);
242 case Compiler.FUNCTION_STRING_LENGTH :
243 return functionStringLength(context);
244 case Compiler.FUNCTION_NORMALIZE_SPACE :
245 return functionNormalizeSpace(context);
246 case Compiler.FUNCTION_TRANSLATE :
247 return functionTranslate(context);
248 case Compiler.FUNCTION_BOOLEAN :
249 return functionBoolean(context);
250 case Compiler.FUNCTION_NOT :
251 return functionNot(context);
252 case Compiler.FUNCTION_TRUE :
253 return functionTrue(context);
254 case Compiler.FUNCTION_FALSE :
255 return functionFalse(context);
256 case Compiler.FUNCTION_NULL :
257 return functionNull(context);
258 case Compiler.FUNCTION_NUMBER :
259 return functionNumber(context);
260 case Compiler.FUNCTION_SUM :
261 return functionSum(context);
262 case Compiler.FUNCTION_FLOOR :
263 return functionFloor(context);
264 case Compiler.FUNCTION_CEILING :
265 return functionCeiling(context);
266 case Compiler.FUNCTION_ROUND :
267 return functionRound(context);
268 case Compiler.FUNCTION_KEY :
269 return functionKey(context);
270 case Compiler.FUNCTION_FORMAT_NUMBER :
271 return functionFormatNumber(context);
272 }
273 return null;
274 }
275
276 protected Object functionLast(EvalContext context) {
277 assertArgCount(0);
278
279
280 int old = context.getCurrentPosition();
281 context.reset();
282 int count = 0;
283 while (context.nextNode()) {
284 count++;
285 }
286
287
288 if (old != 0) {
289 context.setPosition(old);
290 }
291 return new Double(count);
292 }
293
294 protected Object functionPosition(EvalContext context) {
295 assertArgCount(0);
296 return new Integer(context.getCurrentPosition());
297 }
298
299 protected Object functionCount(EvalContext context) {
300 assertArgCount(1);
301 Expression arg1 = getArg1();
302 int count = 0;
303 Object value = arg1.compute(context);
304 if (value instanceof NodePointer) {
305 value = ((NodePointer) value).getValue();
306 }
307 if (value instanceof EvalContext) {
308 EvalContext ctx = (EvalContext) value;
309 while (ctx.hasNext()) {
310 ctx.next();
311 count++;
312 }
313 }
314 else if (value instanceof Collection) {
315 count = ((Collection) value).size();
316 }
317 else if (value == null) {
318 count = 0;
319 }
320 else {
321 count = 1;
322 }
323 return new Double(count);
324 }
325
326 protected Object functionLang(EvalContext context) {
327 assertArgCount(1);
328 String lang = InfoSetUtil.stringValue(getArg1().computeValue(context));
329 NodePointer pointer = (NodePointer) context.getSingleNodePointer();
330 if (pointer == null) {
331 return Boolean.FALSE;
332 }
333 return pointer.isLanguage(lang) ? Boolean.TRUE : Boolean.FALSE;
334 }
335
336 protected Object functionID(EvalContext context) {
337 assertArgCount(1);
338 String id = InfoSetUtil.stringValue(getArg1().computeValue(context));
339 JXPathContext jxpathContext = context.getJXPathContext();
340 NodePointer pointer = (NodePointer) jxpathContext.getContextPointer();
341 return pointer.getPointerByID(jxpathContext, id);
342 }
343
344 protected Object functionKey(EvalContext context) {
345 assertArgCount(2);
346 String key = InfoSetUtil.stringValue(getArg1().computeValue(context));
347 String value = InfoSetUtil.stringValue(getArg2().computeValue(context));
348 JXPathContext jxpathContext = context.getJXPathContext();
349 NodePointer pointer = (NodePointer) jxpathContext.getContextPointer();
350 return pointer.getPointerByKey(jxpathContext, key, value);
351 }
352
353 protected Object functionNamespaceURI(EvalContext context) {
354 if (getArgumentCount() == 0) {
355 NodePointer ptr = context.getCurrentNodePointer();
356 String str = ptr.getNamespaceURI();
357 return str == null ? "" : str;
358 }
359 assertArgCount(1);
360 Object set = getArg1().compute(context);
361 if (set instanceof EvalContext) {
362 EvalContext ctx = (EvalContext) set;
363 if (ctx.hasNext()) {
364 NodePointer ptr = (NodePointer) ctx.next();
365 String str = ptr.getNamespaceURI();
366 return str == null ? "" : str;
367 }
368 }
369 return "";
370 }
371
372 protected Object functionLocalName(EvalContext context) {
373 if (getArgumentCount() == 0) {
374 NodePointer ptr = context.getCurrentNodePointer();
375 return ptr.getName().getName();
376 }
377 assertArgCount(1);
378 Object set = getArg1().compute(context);
379 if (set instanceof EvalContext) {
380 EvalContext ctx = (EvalContext) set;
381 if (ctx.hasNext()) {
382 NodePointer ptr = (NodePointer) ctx.next();
383 return ptr.getName().getName();
384 }
385 }
386 return "";
387 }
388
389 protected Object functionName(EvalContext context) {
390 if (getArgumentCount() == 0) {
391 NodePointer ptr = context.getCurrentNodePointer();
392 return ptr.getName().toString();
393 }
394 assertArgCount(1);
395 Object set = getArg1().compute(context);
396 if (set instanceof EvalContext) {
397 EvalContext ctx = (EvalContext) set;
398 if (ctx.hasNext()) {
399 NodePointer ptr = (NodePointer) ctx.next();
400 return ptr.getName().toString();
401 }
402 }
403 return "";
404 }
405
406 protected Object functionString(EvalContext context) {
407 if (getArgumentCount() == 0) {
408 return InfoSetUtil.stringValue(context.getCurrentNodePointer());
409 }
410 assertArgCount(1);
411 return InfoSetUtil.stringValue(getArg1().computeValue(context));
412 }
413
414 protected Object functionConcat(EvalContext context) {
415 if (getArgumentCount() < 2) {
416 assertArgCount(2);
417 }
418 StringBuffer buffer = new StringBuffer();
419 Expression args[] = getArguments();
420 for (int i = 0; i < args.length; i++) {
421 buffer.append(InfoSetUtil.stringValue(args[i].compute(context)));
422 }
423 return buffer.toString();
424 }
425
426 protected Object functionStartsWith(EvalContext context) {
427 assertArgCount(2);
428 String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
429 String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
430 return s1.startsWith(s2) ? Boolean.TRUE : Boolean.FALSE;
431 }
432
433 protected Object functionContains(EvalContext context) {
434 assertArgCount(2);
435 String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
436 String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
437 return s1.indexOf(s2) != -1 ? Boolean.TRUE : Boolean.FALSE;
438 }
439
440 protected Object functionSubstringBefore(EvalContext context) {
441 assertArgCount(2);
442 String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
443 String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
444 int index = s1.indexOf(s2);
445 if (index == -1) {
446 return "";
447 }
448 return s1.substring(0, index);
449 }
450
451 protected Object functionSubstringAfter(EvalContext context) {
452 assertArgCount(2);
453 String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
454 String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
455 int index = s1.indexOf(s2);
456 if (index == -1) {
457 return "";
458 }
459 return s1.substring(index + s2.length());
460 }
461
462 protected Object functionSubstring(EvalContext context) {
463 int ac = getArgumentCount();
464 if (ac != 2 && ac != 3) {
465 assertArgCount(2);
466 }
467
468 String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
469 double from = InfoSetUtil.doubleValue(getArg2().computeValue(context));
470 if (Double.isNaN(from)) {
471 return "";
472 }
473
474 from = Math.round(from);
475 if (ac == 2) {
476 if (from < 1) {
477 from = 1;
478 }
479 return s1.substring((int) from - 1);
480 }
481 else {
482 double length =
483 InfoSetUtil.doubleValue(getArg3().computeValue(context));
484 length = Math.round(length);
485 if (length < 0) {
486 return "";
487 }
488
489 double to = from + length;
490 if (to < 1) {
491 return "";
492 }
493
494 if (to > s1.length() + 1) {
495 if (from < 1) {
496 from = 1;
497 }
498 return s1.substring((int) from - 1);
499 }
500
501 if (from < 1) {
502 from = 1;
503 }
504 return s1.substring((int) from - 1, (int) (to - 1));
505 }
506 }
507
508 protected Object functionStringLength(EvalContext context) {
509 String s;
510 if (getArgumentCount() == 0) {
511 s = InfoSetUtil.stringValue(context.getCurrentNodePointer());
512 }
513 else {
514 assertArgCount(1);
515 s = InfoSetUtil.stringValue(getArg1().computeValue(context));
516 }
517 return new Double(s.length());
518 }
519
520 protected Object functionNormalizeSpace(EvalContext context) {
521 assertArgCount(1);
522 String s = InfoSetUtil.stringValue(getArg1().computeValue(context));
523 char chars[] = s.toCharArray();
524 int out = 0;
525 int phase = 0;
526 for (int in = 0; in < chars.length; in++) {
527 switch(chars[in]) {
528 case 0x20:
529 case 0x9:
530 case 0xD:
531 case 0xA:
532 if (phase == 0) {
533 ;
534 }
535 else if (phase == 1) {
536 phase = 2;
537 chars[out++] = ' ';
538 }
539 break;
540 default:
541 chars[out++] = chars[in];
542 phase = 1;
543 }
544 }
545 if (phase == 2) {
546 out--;
547 }
548 return new String(chars, 0, out);
549 }
550
551 protected Object functionTranslate(EvalContext context) {
552 assertArgCount(3);
553 String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
554 String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
555 String s3 = InfoSetUtil.stringValue(getArg3().computeValue(context));
556 char chars[] = s1.toCharArray();
557 int out = 0;
558 for (int in = 0; in < chars.length; in++) {
559 char c = chars[in];
560 int inx = s2.indexOf(c);
561 if (inx != -1) {
562 if (inx < s3.length()) {
563 chars[out++] = s3.charAt(inx);
564 }
565 }
566 else {
567 chars[out++] = c;
568 }
569 }
570 return new String(chars, 0, out);
571 }
572
573 protected Object functionBoolean(EvalContext context) {
574 assertArgCount(1);
575 return InfoSetUtil.booleanValue(getArg1().computeValue(context))
576 ? Boolean.TRUE
577 : Boolean.FALSE;
578 }
579
580 protected Object functionNot(EvalContext context) {
581 assertArgCount(1);
582 return InfoSetUtil.booleanValue(getArg1().computeValue(context))
583 ? Boolean.FALSE
584 : Boolean.TRUE;
585 }
586
587 protected Object functionTrue(EvalContext context) {
588 assertArgCount(0);
589 return Boolean.TRUE;
590 }
591
592 protected Object functionFalse(EvalContext context) {
593 assertArgCount(0);
594 return Boolean.FALSE;
595 }
596
597 protected Object functionNull(EvalContext context) {
598 assertArgCount(0);
599 return null;
600 }
601
602 protected Object functionNumber(EvalContext context) {
603 if (getArgumentCount() == 0) {
604 return InfoSetUtil.number(context.getCurrentNodePointer());
605 }
606 assertArgCount(1);
607 return InfoSetUtil.number(getArg1().computeValue(context));
608 }
609
610 protected Object functionSum(EvalContext context) {
611 assertArgCount(1);
612 Object v = getArg1().compute(context);
613 if (v == null) {
614 return ZERO;
615 }
616 else if (v instanceof EvalContext) {
617 double sum = 0.0;
618 EvalContext ctx = (EvalContext) v;
619 while (ctx.hasNext()) {
620 NodePointer ptr = (NodePointer) ctx.next();
621 sum += InfoSetUtil.doubleValue(ptr);
622 }
623 return new Double(sum);
624 }
625 throw new JXPathException(
626 "Invalid argument type for 'sum': " + v.getClass().getName());
627 }
628
629 protected Object functionFloor(EvalContext context) {
630 assertArgCount(1);
631 double v = InfoSetUtil.doubleValue(getArg1().computeValue(context));
632 return new Double(Math.floor(v));
633 }
634
635 protected Object functionCeiling(EvalContext context) {
636 assertArgCount(1);
637 double v = InfoSetUtil.doubleValue(getArg1().computeValue(context));
638 return new Double(Math.ceil(v));
639 }
640
641 protected Object functionRound(EvalContext context) {
642 assertArgCount(1);
643 double v = InfoSetUtil.doubleValue(getArg1().computeValue(context));
644 return new Double(Math.round(v));
645 }
646
647 private Object functionFormatNumber(EvalContext context) {
648 int ac = getArgumentCount();
649 if (ac != 2 && ac != 3) {
650 assertArgCount(2);
651 }
652
653 double number =
654 InfoSetUtil.doubleValue(getArg1().computeValue(context));
655 String pattern =
656 InfoSetUtil.stringValue(getArg2().computeValue(context));
657
658 DecimalFormatSymbols symbols = null;
659 if (ac == 3) {
660 String symbolsName =
661 InfoSetUtil.stringValue(getArg3().computeValue(context));
662 symbols =
663 context.getJXPathContext().getDecimalFormatSymbols(symbolsName);
664 }
665 else {
666 NodePointer pointer = context.getCurrentNodePointer();
667 Locale locale;
668 if (pointer != null) {
669 locale = pointer.getLocale();
670 }
671 else {
672 locale = context.getJXPathContext().getLocale();
673 }
674 symbols = new DecimalFormatSymbols(locale);
675 }
676
677 DecimalFormat format = (DecimalFormat) NumberFormat.getInstance();
678 format.setDecimalFormatSymbols(symbols);
679 format.applyLocalizedPattern(pattern);
680 return format.format(number);
681 }
682
683 private void assertArgCount(int count) {
684 if (getArgumentCount() != count) {
685 throw new JXPathException("Incorrect number of argument: " + this);
686 }
687 }
688 }