001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jxpath.ri.compiler;
017:
018: import java.text.DecimalFormat;
019: import java.text.DecimalFormatSymbols;
020: import java.text.NumberFormat;
021: import java.util.Collection;
022: import java.util.Locale;
023:
024: import org.apache.commons.jxpath.JXPathContext;
025: import org.apache.commons.jxpath.JXPathException;
026: import org.apache.commons.jxpath.ri.Compiler;
027: import org.apache.commons.jxpath.ri.EvalContext;
028: import org.apache.commons.jxpath.ri.InfoSetUtil;
029: import org.apache.commons.jxpath.ri.model.NodePointer;
030:
031: /**
032: * An element of the compile tree representing one of built-in functions
033: * like "position()" or "number()".
034: *
035: * @author Dmitri Plotnikov
036: * @version $Revision: 1.16 $ $Date: 2004/04/01 02:53:45 $
037: */
038: public class CoreFunction extends Operation {
039:
040: private static final Double ZERO = new Double(0);
041: private int functionCode;
042:
043: public CoreFunction(int functionCode, Expression args[]) {
044: super (args);
045: this .functionCode = functionCode;
046: }
047:
048: public int getFunctionCode() {
049: return functionCode;
050: }
051:
052: protected String getFunctionName() {
053: switch (functionCode) {
054: case Compiler.FUNCTION_LAST:
055: return "last";
056: case Compiler.FUNCTION_POSITION:
057: return "position";
058: case Compiler.FUNCTION_COUNT:
059: return "count";
060: case Compiler.FUNCTION_ID:
061: return "id";
062: case Compiler.FUNCTION_LOCAL_NAME:
063: return "local-name";
064: case Compiler.FUNCTION_NAMESPACE_URI:
065: return "namespace-uri";
066: case Compiler.FUNCTION_NAME:
067: return "name";
068: case Compiler.FUNCTION_STRING:
069: return "string";
070: case Compiler.FUNCTION_CONCAT:
071: return "concat";
072: case Compiler.FUNCTION_STARTS_WITH:
073: return "starts-with";
074: case Compiler.FUNCTION_CONTAINS:
075: return "contains";
076: case Compiler.FUNCTION_SUBSTRING_BEFORE:
077: return "substring-before";
078: case Compiler.FUNCTION_SUBSTRING_AFTER:
079: return "substring-after";
080: case Compiler.FUNCTION_SUBSTRING:
081: return "substring";
082: case Compiler.FUNCTION_STRING_LENGTH:
083: return "string-length";
084: case Compiler.FUNCTION_NORMALIZE_SPACE:
085: return "normalize-space";
086: case Compiler.FUNCTION_TRANSLATE:
087: return "translate";
088: case Compiler.FUNCTION_BOOLEAN:
089: return "boolean";
090: case Compiler.FUNCTION_NOT:
091: return "not";
092: case Compiler.FUNCTION_TRUE:
093: return "true";
094: case Compiler.FUNCTION_FALSE:
095: return "false";
096: case Compiler.FUNCTION_LANG:
097: return "lang";
098: case Compiler.FUNCTION_NUMBER:
099: 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: // Move the position to the beginning and iterate through
279: // the context to count nodes.
280: int old = context.getCurrentPosition();
281: context.reset();
282: int count = 0;
283: while (context.nextNode()) {
284: count++;
285: }
286:
287: // Restore the current position.
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: } else if (value instanceof Collection) {
314: count = ((Collection) value).size();
315: } else if (value == null) {
316: count = 0;
317: } else {
318: count = 1;
319: }
320: return new Double(count);
321: }
322:
323: protected Object functionLang(EvalContext context) {
324: assertArgCount(1);
325: String lang = InfoSetUtil.stringValue(getArg1().computeValue(
326: context));
327: NodePointer pointer = (NodePointer) context
328: .getSingleNodePointer();
329: if (pointer == null) {
330: return Boolean.FALSE;
331: }
332: return pointer.isLanguage(lang) ? Boolean.TRUE : Boolean.FALSE;
333: }
334:
335: protected Object functionID(EvalContext context) {
336: assertArgCount(1);
337: String id = InfoSetUtil.stringValue(getArg1().computeValue(
338: context));
339: JXPathContext jxpathContext = context.getJXPathContext();
340: NodePointer pointer = (NodePointer) jxpathContext
341: .getContextPointer();
342: return pointer.getPointerByID(jxpathContext, id);
343: }
344:
345: protected Object functionKey(EvalContext context) {
346: assertArgCount(2);
347: String key = InfoSetUtil.stringValue(getArg1().computeValue(
348: context));
349: String value = InfoSetUtil.stringValue(getArg2().computeValue(
350: context));
351: JXPathContext jxpathContext = context.getJXPathContext();
352: NodePointer pointer = (NodePointer) jxpathContext
353: .getContextPointer();
354: return pointer.getPointerByKey(jxpathContext, key, value);
355: }
356:
357: protected Object functionNamespaceURI(EvalContext context) {
358: if (getArgumentCount() == 0) {
359: NodePointer ptr = context.getCurrentNodePointer();
360: String str = ptr.getNamespaceURI();
361: return str == null ? "" : str;
362: }
363: assertArgCount(1);
364: Object set = getArg1().compute(context);
365: if (set instanceof EvalContext) {
366: EvalContext ctx = (EvalContext) set;
367: if (ctx.hasNext()) {
368: NodePointer ptr = (NodePointer) ctx.next();
369: String str = ptr.getNamespaceURI();
370: return str == null ? "" : str;
371: }
372: }
373: return "";
374: }
375:
376: protected Object functionLocalName(EvalContext context) {
377: if (getArgumentCount() == 0) {
378: NodePointer ptr = context.getCurrentNodePointer();
379: return ptr.getName().getName();
380: }
381: assertArgCount(1);
382: Object set = getArg1().compute(context);
383: if (set instanceof EvalContext) {
384: EvalContext ctx = (EvalContext) set;
385: if (ctx.hasNext()) {
386: NodePointer ptr = (NodePointer) ctx.next();
387: return ptr.getName().getName();
388: }
389: }
390: return "";
391: }
392:
393: protected Object functionName(EvalContext context) {
394: if (getArgumentCount() == 0) {
395: NodePointer ptr = context.getCurrentNodePointer();
396: return ptr.getName().toString();
397: }
398: assertArgCount(1);
399: Object set = getArg1().compute(context);
400: if (set instanceof EvalContext) {
401: EvalContext ctx = (EvalContext) set;
402: if (ctx.hasNext()) {
403: NodePointer ptr = (NodePointer) ctx.next();
404: return ptr.getName().toString();
405: }
406: }
407: return "";
408: }
409:
410: protected Object functionString(EvalContext context) {
411: if (getArgumentCount() == 0) {
412: return InfoSetUtil.stringValue(context
413: .getCurrentNodePointer());
414: }
415: assertArgCount(1);
416: return InfoSetUtil.stringValue(getArg1().computeValue(context));
417: }
418:
419: protected Object functionConcat(EvalContext context) {
420: if (getArgumentCount() < 2) {
421: assertArgCount(2);
422: }
423: StringBuffer buffer = new StringBuffer();
424: Expression args[] = getArguments();
425: for (int i = 0; i < args.length; i++) {
426: buffer.append(InfoSetUtil.stringValue(args[i]
427: .compute(context)));
428: }
429: return buffer.toString();
430: }
431:
432: protected Object functionStartsWith(EvalContext context) {
433: assertArgCount(2);
434: String s1 = InfoSetUtil.stringValue(getArg1().computeValue(
435: context));
436: String s2 = InfoSetUtil.stringValue(getArg2().computeValue(
437: context));
438: return s1.startsWith(s2) ? Boolean.TRUE : Boolean.FALSE;
439: }
440:
441: protected Object functionContains(EvalContext context) {
442: assertArgCount(2);
443: String s1 = InfoSetUtil.stringValue(getArg1().computeValue(
444: context));
445: String s2 = InfoSetUtil.stringValue(getArg2().computeValue(
446: context));
447: return s1.indexOf(s2) != -1 ? Boolean.TRUE : Boolean.FALSE;
448: }
449:
450: protected Object functionSubstringBefore(EvalContext context) {
451: assertArgCount(2);
452: String s1 = InfoSetUtil.stringValue(getArg1().computeValue(
453: context));
454: String s2 = InfoSetUtil.stringValue(getArg2().computeValue(
455: context));
456: int index = s1.indexOf(s2);
457: if (index == -1) {
458: return "";
459: }
460: return s1.substring(0, index);
461: }
462:
463: protected Object functionSubstringAfter(EvalContext context) {
464: assertArgCount(2);
465: String s1 = InfoSetUtil.stringValue(getArg1().computeValue(
466: context));
467: String s2 = InfoSetUtil.stringValue(getArg2().computeValue(
468: context));
469: int index = s1.indexOf(s2);
470: if (index == -1) {
471: return "";
472: }
473: return s1.substring(index + s2.length());
474: }
475:
476: protected Object functionSubstring(EvalContext context) {
477: int ac = getArgumentCount();
478: if (ac != 2 && ac != 3) {
479: assertArgCount(2);
480: }
481:
482: String s1 = InfoSetUtil.stringValue(getArg1().computeValue(
483: context));
484: double from = InfoSetUtil.doubleValue(getArg2().computeValue(
485: context));
486: if (Double.isNaN(from)) {
487: return "";
488: }
489:
490: from = Math.round(from);
491: if (ac == 2) {
492: if (from < 1) {
493: from = 1;
494: }
495: return s1.substring((int) from - 1);
496: } else {
497: double length = InfoSetUtil.doubleValue(getArg3()
498: .computeValue(context));
499: length = Math.round(length);
500: if (length < 0) {
501: return "";
502: }
503:
504: double to = from + length;
505: if (to < 1) {
506: return "";
507: }
508:
509: if (to > s1.length() + 1) {
510: if (from < 1) {
511: from = 1;
512: }
513: return s1.substring((int) from - 1);
514: }
515:
516: if (from < 1) {
517: from = 1;
518: }
519: return s1.substring((int) from - 1, (int) (to - 1));
520: }
521: }
522:
523: protected Object functionStringLength(EvalContext context) {
524: String s;
525: if (getArgumentCount() == 0) {
526: s = InfoSetUtil
527: .stringValue(context.getCurrentNodePointer());
528: } else {
529: assertArgCount(1);
530: s = InfoSetUtil
531: .stringValue(getArg1().computeValue(context));
532: }
533: return new Double(s.length());
534: }
535:
536: protected Object functionNormalizeSpace(EvalContext context) {
537: assertArgCount(1);
538: String s = InfoSetUtil.stringValue(getArg1().computeValue(
539: context));
540: char chars[] = s.toCharArray();
541: int out = 0;
542: int phase = 0;
543: for (int in = 0; in < chars.length; in++) {
544: switch (chars[in]) {
545: case 0x20:
546: case 0x9:
547: case 0xD:
548: case 0xA:
549: if (phase == 0) { // beginning
550: ;
551: } else if (phase == 1) { // non-space
552: phase = 2;
553: chars[out++] = ' ';
554: }
555: break;
556: default:
557: chars[out++] = chars[in];
558: phase = 1;
559: }
560: }
561: if (phase == 2) { // trailing-space
562: out--;
563: }
564: return new String(chars, 0, out);
565: }
566:
567: protected Object functionTranslate(EvalContext context) {
568: assertArgCount(3);
569: String s1 = InfoSetUtil.stringValue(getArg1().computeValue(
570: context));
571: String s2 = InfoSetUtil.stringValue(getArg2().computeValue(
572: context));
573: String s3 = InfoSetUtil.stringValue(getArg3().computeValue(
574: context));
575: char chars[] = s1.toCharArray();
576: int out = 0;
577: for (int in = 0; in < chars.length; in++) {
578: char c = chars[in];
579: int inx = s2.indexOf(c);
580: if (inx != -1) {
581: if (inx < s3.length()) {
582: chars[out++] = s3.charAt(inx);
583: }
584: } else {
585: chars[out++] = c;
586: }
587: }
588: return new String(chars, 0, out);
589: }
590:
591: protected Object functionBoolean(EvalContext context) {
592: assertArgCount(1);
593: return InfoSetUtil
594: .booleanValue(getArg1().computeValue(context)) ? Boolean.TRUE
595: : Boolean.FALSE;
596: }
597:
598: protected Object functionNot(EvalContext context) {
599: assertArgCount(1);
600: return InfoSetUtil
601: .booleanValue(getArg1().computeValue(context)) ? Boolean.FALSE
602: : Boolean.TRUE;
603: }
604:
605: protected Object functionTrue(EvalContext context) {
606: assertArgCount(0);
607: return Boolean.TRUE;
608: }
609:
610: protected Object functionFalse(EvalContext context) {
611: assertArgCount(0);
612: return Boolean.FALSE;
613: }
614:
615: protected Object functionNull(EvalContext context) {
616: assertArgCount(0);
617: return null;
618: }
619:
620: protected Object functionNumber(EvalContext context) {
621: if (getArgumentCount() == 0) {
622: return InfoSetUtil.number(context.getCurrentNodePointer());
623: }
624: assertArgCount(1);
625: return InfoSetUtil.number(getArg1().computeValue(context));
626: }
627:
628: protected Object functionSum(EvalContext context) {
629: assertArgCount(1);
630: Object v = getArg1().compute(context);
631: if (v == null) {
632: return ZERO;
633: } else if (v instanceof EvalContext) {
634: double sum = 0.0;
635: EvalContext ctx = (EvalContext) v;
636: while (ctx.hasNext()) {
637: NodePointer ptr = (NodePointer) ctx.next();
638: sum += InfoSetUtil.doubleValue(ptr);
639: }
640: return new Double(sum);
641: }
642: throw new JXPathException("Invalid argument type for 'sum': "
643: + v.getClass().getName());
644: }
645:
646: protected Object functionFloor(EvalContext context) {
647: assertArgCount(1);
648: double v = InfoSetUtil.doubleValue(getArg1().computeValue(
649: context));
650: return new Double(Math.floor(v));
651: }
652:
653: protected Object functionCeiling(EvalContext context) {
654: assertArgCount(1);
655: double v = InfoSetUtil.doubleValue(getArg1().computeValue(
656: context));
657: return new Double(Math.ceil(v));
658: }
659:
660: protected Object functionRound(EvalContext context) {
661: assertArgCount(1);
662: double v = InfoSetUtil.doubleValue(getArg1().computeValue(
663: context));
664: return new Double(Math.round(v));
665: }
666:
667: private Object functionFormatNumber(EvalContext context) {
668: int ac = getArgumentCount();
669: if (ac != 2 && ac != 3) {
670: assertArgCount(2);
671: }
672:
673: double number = InfoSetUtil.doubleValue(getArg1().computeValue(
674: context));
675: String pattern = InfoSetUtil.stringValue(getArg2()
676: .computeValue(context));
677:
678: DecimalFormatSymbols symbols = null;
679: if (ac == 3) {
680: String symbolsName = InfoSetUtil.stringValue(getArg3()
681: .computeValue(context));
682: symbols = context.getJXPathContext()
683: .getDecimalFormatSymbols(symbolsName);
684: } else {
685: NodePointer pointer = context.getCurrentNodePointer();
686: Locale locale;
687: if (pointer != null) {
688: locale = pointer.getLocale();
689: } else {
690: locale = context.getJXPathContext().getLocale();
691: }
692: symbols = new DecimalFormatSymbols(locale);
693: }
694:
695: DecimalFormat format = (DecimalFormat) NumberFormat
696: .getInstance();
697: format.setDecimalFormatSymbols(symbols);
698: format.applyLocalizedPattern(pattern);
699: return format.format(number);
700: }
701:
702: private void assertArgCount(int count) {
703: if (getArgumentCount() != count) {
704: throw new JXPathException("Incorrect number of argument: "
705: + this);
706: }
707: }
708: }
|