001: package org.apache.velocity.runtime.parser.node;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import org.apache.velocity.context.Context;
023: import org.apache.velocity.exception.MethodInvocationException;
024: import org.apache.velocity.runtime.parser.Token;
025:
026: /**
027: * Utilities for dealing with the AST node structure.
028: *
029: * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
030: * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
031: * @version $Id: NodeUtils.java 463298 2006-10-12 16:10:32Z henning $
032: */
033: public class NodeUtils {
034: /**
035: * Collect all the <SPECIAL_TOKEN>s that
036: * are carried along with a token. Special
037: * tokens do not participate in parsing but
038: * can still trigger certain lexical actions.
039: * In some cases you may want to retrieve these
040: * special tokens, this is simply a way to
041: * extract them.
042: * @param t
043: * @return String with the special tokens.
044: */
045: public static String specialText(Token t) {
046: StringBuffer specialText = new StringBuffer();
047:
048: if (t.specialToken == null
049: || t.specialToken.image.startsWith("##")) {
050: return "";
051: }
052:
053: Token tmp_t = t.specialToken;
054:
055: while (tmp_t.specialToken != null) {
056: tmp_t = tmp_t.specialToken;
057: }
058:
059: while (tmp_t != null) {
060: String st = tmp_t.image;
061:
062: StringBuffer sb = new StringBuffer();
063:
064: for (int i = 0; i < st.length(); i++) {
065: char c = st.charAt(i);
066:
067: if (c == '#' || c == '$') {
068: sb.append(c);
069: }
070:
071: /*
072: * more dreaded MORE hack :)
073: *
074: * looking for ("\\")*"$" sequences
075: */
076:
077: if (c == '\\') {
078: boolean ok = true;
079: boolean term = false;
080:
081: int j = i;
082: for (ok = true; ok && j < st.length(); j++) {
083: char cc = st.charAt(j);
084:
085: if (cc == '\\') {
086: /*
087: * if we see a \, keep going
088: */
089: continue;
090: } else if (cc == '$') {
091: /*
092: * a $ ends it correctly
093: */
094: term = true;
095: ok = false;
096: } else {
097: /*
098: * nah...
099: */
100: ok = false;
101: }
102: }
103:
104: if (term) {
105: String foo = st.substring(i, j);
106: sb.append(foo);
107: i = j;
108: }
109: }
110: }
111:
112: // This is a potential JDK 1.3/JDK 1.4 gotcha. If we remove
113: // the toString() method call, then when compiling under JDK 1.4,
114: // this will be mapped to StringBuffer.append(StringBuffer) and
115: // under JDK 1.3, it will be mapped to StringBuffer.append(Object).
116: // So the JDK 1.4 compiled jar will bomb out under JDK 1.3 with a
117: // MethodNotFound error.
118: //
119: // @todo Once we are JDK 1.4+ only, remove the toString(), make this
120: // loop perform a little bit better.
121: specialText.append(sb.toString());
122:
123: tmp_t = tmp_t.next;
124: }
125:
126: return specialText.toString();
127: }
128:
129: /**
130: * complete node literal
131: * @param t
132: * @return A node literal.
133: *
134: */
135: public static String tokenLiteral(Token t) {
136: return specialText(t) + t.image;
137: }
138:
139: /**
140: * Utility method to interpolate context variables
141: * into string literals. So that the following will
142: * work:
143: *
144: * #set $name = "candy"
145: * $image.getURI("${name}.jpg")
146: *
147: * And the string literal argument will
148: * be transformed into "candy.jpg" before
149: * the method is executed.
150: * @param argStr
151: * @param vars
152: * @return Interpoliation result.
153: * @throws MethodInvocationException
154: */
155: public static String interpolate(String argStr, Context vars)
156: throws MethodInvocationException {
157: StringBuffer argBuf = new StringBuffer();
158:
159: for (int cIdx = 0; cIdx < argStr.length();) {
160: char ch = argStr.charAt(cIdx);
161:
162: switch (ch) {
163: case '$':
164: StringBuffer nameBuf = new StringBuffer();
165: for (++cIdx; cIdx < argStr.length(); ++cIdx) {
166: ch = argStr.charAt(cIdx);
167: if (ch == '_' || ch == '-'
168: || Character.isLetterOrDigit(ch))
169: nameBuf.append(ch);
170: else if (ch == '{' || ch == '}')
171: continue;
172: else
173: break;
174: }
175:
176: if (nameBuf.length() > 0) {
177: Object value = vars.get(nameBuf.toString());
178:
179: if (value == null)
180: argBuf.append("$").append(nameBuf.toString());
181: else
182: argBuf.append(value.toString());
183: }
184: break;
185:
186: default:
187: argBuf.append(ch);
188: ++cIdx;
189: break;
190: }
191: }
192:
193: return argBuf.toString();
194: }
195: }
|