001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.xpath.expr;
030:
031: import com.caucho.util.CharBuffer;
032: import com.caucho.xml.XmlChar;
033: import com.caucho.xpath.Expr;
034: import com.caucho.xpath.ExprEnvironment;
035: import com.caucho.xpath.XPathException;
036:
037: import org.w3c.dom.Node;
038:
039: import java.util.ArrayList;
040: import java.util.Iterator;
041:
042: /**
043: * Implements the builtin XPath string expressions.
044: */
045: public class StringExpr extends Expr {
046: // Code of the expression defined in Expr
047: private int _code;
048: // First argument
049: private Expr _left;
050: // Second argument
051: private Expr _right;
052: // Third
053: private Expr _third;
054: private String _value;
055: // Arguments for more than 3.
056: private ArrayList _args;
057:
058: /**
059: * Create a StringExpression with three arguments.
060: *
061: * @param code the Expr code of the expression.
062: * @param left the first argument.
063: * @param right the second argument.
064: * @param third the third argument.
065: */
066: public StringExpr(int code, Expr left, Expr right, Expr third) {
067: _code = code;
068: _left = left;
069: _right = right;
070: _third = third;
071: }
072:
073: public StringExpr(int code, Expr left, Expr right) {
074: _code = code;
075: _left = left;
076: _right = right;
077: }
078:
079: public StringExpr(int code, Expr expr) {
080: _code = code;
081: _left = expr;
082: }
083:
084: public StringExpr(String value) {
085: _code = CONST;
086: _value = value;
087: }
088:
089: /**
090: * Creates a string expression from a list of arguments.
091: *
092: * @param code Expr code for the function.
093: * @param args array list of the arguments.
094: */
095: public StringExpr(int code, ArrayList args) {
096: _code = code;
097: _args = args;
098:
099: if (args.size() > 0)
100: _left = (Expr) args.get(0);
101: if (args.size() > 1)
102: _right = (Expr) args.get(1);
103: if (args.size() > 2)
104: _third = (Expr) args.get(2);
105: }
106:
107: /**
108: * The StringExpr returns a string value.
109: */
110: public boolean isString() {
111: return true;
112: }
113:
114: public String getValue() {
115: return _value;
116: }
117:
118: /**
119: * Evaluates the expression as an string.
120: *
121: * @param node the current node
122: * @param env the variable environment.
123: *
124: * @return the string representation of the expression.
125: */
126: public String evalString(Node node, ExprEnvironment env)
127: throws XPathException {
128: switch (_code) {
129: case CONST:
130: return _value;
131:
132: case STRING:
133: return _left.evalString(node, env);
134:
135: case CONCAT:
136: CharBuffer cb = CharBuffer.allocate();
137: for (int i = 0; i < _args.size(); i++)
138: ((Expr) _args.get(i)).evalString(cb, node, env);
139: return cb.close();
140:
141: case SUBSTRING_BEFORE:
142: String lstr = _left.evalString(node, env);
143: String rstr = _right.evalString(node, env);
144: int index = lstr.indexOf(rstr);
145: return index > 0 ? lstr.substring(0, index) : "";
146:
147: case SUBSTRING_AFTER:
148: lstr = _left.evalString(node, env);
149: rstr = _right.evalString(node, env);
150: index = lstr.indexOf(rstr);
151: return index >= 0 ? lstr.substring(index + rstr.length())
152: : "";
153:
154: case NORMALIZE:
155: lstr = _left.evalString(node, env);
156: return normalize(lstr);
157:
158: case TRANSLATE:
159: lstr = _left.evalString(node, env);
160: rstr = _right.evalString(node, env);
161: String tstr = _third.evalString(node, env);
162: return translate(lstr, rstr, tstr);
163:
164: case FORMAT_NUMBER:
165: return _left.evalString(node, env);
166:
167: case LOCAL_PART:
168: Object lobj = _left.evalObject(node, env);
169: Node nodeValue = toNode(lobj);
170: if (nodeValue != null)
171: return nodeValue.getLocalName();
172: else
173: return "";
174:
175: case NAMESPACE:
176: lobj = _left.evalObject(node, env);
177: nodeValue = toNode(lobj);
178: if (nodeValue != null) {
179: String uri = nodeValue.getNamespaceURI();
180: return uri != null ? uri : "";
181: } else
182: return "";
183:
184: case QNAME:
185: lobj = _left.evalObject(node, env);
186: nodeValue = toNode(lobj);
187: if (nodeValue != null)
188: return nodeValue.getNodeName();
189: else
190: return "";
191:
192: case GENERATE_ID:
193: Iterator iter = _left.evalNodeSet(node, env);
194: return "G"
195: + String.valueOf(System.identityHashCode(iter
196: .next()));
197:
198: case SYSTEM_PROPERTY:
199: lstr = _left.evalString(node, env);
200: if (lstr == null)
201: return "";
202: else if (lstr.equals("xsl:version"))
203: return "1.0";
204: else
205: return "";
206:
207: case SUBSTRING:
208: lstr = _left.evalString(node, env);
209: if (lstr == null)
210: lstr = "";
211: double start = _right.evalNumber(node, env) - 1;
212:
213: double end = lstr.length();
214: if (_third != null)
215: end = Math.round(start) + _third.evalNumber(node, env);
216:
217: if (Double.isNaN(end) || Double.isNaN(start)) {
218: start = 0;
219: end = 0;
220: }
221:
222: if (start < 0)
223: start = 0;
224: else if (lstr.length() < start)
225: start = lstr.length();
226:
227: if (end < 0)
228: end = 0;
229: else if (end < start)
230: end = start;
231: else if (lstr.length() < end)
232: end = lstr.length();
233:
234: return lstr.substring((int) (start + 0.5),
235: (int) (end + 0.5));
236:
237: default:
238: throw new RuntimeException("unknown code: " + _code);
239: }
240: }
241:
242: /**
243: * Evaluate the expression as a boolean, i.e. evaluate it as a string
244: * and then convert it to a boolean.
245: *
246: * @param node the current node
247: * @param env the variable environment.
248: *
249: * @return the boolean representation of the expression.
250: */
251: public boolean evalBoolean(Node node, ExprEnvironment env)
252: throws XPathException {
253: String string = evalString(node, env);
254:
255: return string != null && string.length() > 0;
256: }
257:
258: /**
259: * Evaluate the expression as a double, i.e. evaluate it as a string
260: * and then convert it to a double.
261: *
262: * @param node the current node
263: * @param env the variable environment.
264: *
265: * @return the numeric representation of the expression.
266: */
267: public double evalNumber(Node node, ExprEnvironment env)
268: throws XPathException {
269: return stringToNumber(evalString(node, env));
270: }
271:
272: /**
273: * Evaluate the expression as an object, i.e. return the string value.
274: *
275: * @param node the current node
276: * @param env the variable environment.
277: *
278: * @return the boolean representation of the expression.
279: */
280: public Object evalObject(Node node, ExprEnvironment env)
281: throws XPathException {
282: return evalString(node, env);
283: }
284:
285: /**
286: * Normalize the string, converting all whitespace to a space and
287: * eliminating consecutive spaces.
288: */
289: private String normalize(String string) {
290: CharBuffer result = new CharBuffer();
291:
292: int i = 0;
293: int len = string.length();
294: for (; i < len && XmlChar.isWhitespace(string.charAt(i)); i++) {
295: }
296:
297: boolean lastIsWhitespace = false;
298: for (; i < len; i++) {
299: if (XmlChar.isWhitespace(string.charAt(i))) {
300: lastIsWhitespace = true;
301: } else if (lastIsWhitespace) {
302: result.append(' ');
303: result.append(string.charAt(i));
304: lastIsWhitespace = false;
305: } else
306: result.append(string.charAt(i));
307: }
308:
309: return result.toString();
310: }
311:
312: /**
313: * Translate the string, converting characters. translate("foo", "f", "b")
314: * returns "boo".
315: *
316: * @param string the string to translate.
317: * @param from characters to convert from.
318: * @param to the replacement characters.
319: */
320: private String translate(String string, String from, String to) {
321: CharBuffer result = new CharBuffer();
322:
323: loop: for (int i = 0; i < string.length(); i++) {
324: char ch = string.charAt(i);
325:
326: for (int j = 0; j < from.length(); j++) {
327: if (ch == from.charAt(j)) {
328: if (to.length() > j)
329: result.append(to.charAt(j));
330: continue loop;
331: }
332: }
333:
334: result.append(ch);
335: }
336:
337: return result.toString();
338: }
339:
340: /**
341: * Return the expression as a string. toString() returns a valid
342: * XPath expression. This lets applications like XSLT use toString()
343: * to print the string in the generated Java.
344: */
345: public String toString() {
346: switch (_code) {
347: case CONST:
348: CharBuffer cb = CharBuffer.allocate();
349: cb.append("'");
350: for (int i = 0; i < _value.length(); i++) {
351: char ch = _value.charAt(i);
352: switch (ch) {
353: case '\n':
354: cb.append("\\n");
355: break;
356: case '\r':
357: cb.append("\\r");
358: break;
359: case '\\':
360: cb.append("\\\\");
361: break;
362: case '\'':
363: cb.append("\\'\\'");
364: break;
365: case '"':
366: cb.append("\\\"");
367: break;
368: default:
369: cb.append(ch);
370: }
371: }
372: cb.append("'");
373: return cb.toString();
374:
375: case STRING:
376: return "string(" + _left + ")";
377:
378: case CONCAT:
379: String result = "concat(";
380: for (int i = 0; i < _args.size(); i++) {
381: if (i > 0)
382: result = result + ", ";
383: result = result + _args.get(i);
384: }
385: return result + ")";
386:
387: case SUBSTRING_BEFORE:
388: return "substring-before(" + _left + ", " + _right + ")";
389:
390: case SUBSTRING_AFTER:
391: return "substring-after(" + _left + ", " + _right + ")";
392:
393: case NORMALIZE:
394: return "normalize-space(" + _left + ")";
395:
396: case TRANSLATE:
397: return "translate(" + _left + ", " + _right + ", " + _third
398: + ")";
399:
400: case FORMAT_NUMBER:
401: return "format-number(" + _left + ")";
402:
403: case LOCAL_PART:
404: return "local-part(" + _left + ")";
405:
406: case NAMESPACE:
407: return "namespace-uri(" + _left + ")";
408:
409: case QNAME:
410: return "name(" + _left + ")";
411:
412: case GENERATE_ID:
413: return "generate-id(" + _left + ")";
414:
415: case SYSTEM_PROPERTY:
416: return "system-property(" + _left + ")";
417:
418: case SUBSTRING:
419: return "substring(" + _left + "," + _right
420: + (_third == null ? "" : ("," + _third)) + ")";
421:
422: case BASE_URI:
423: return "fn:base-uri(" + _left + ")";
424:
425: default:
426: return super.toString();
427: }
428: }
429: }
|