1 package org.apache.velocity.runtime.parser.node;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import org.apache.velocity.context.Context;
23 import org.apache.velocity.exception.MethodInvocationException;
24 import org.apache.velocity.runtime.parser.Token;
25
26 /**
27 * Utilities for dealing with the AST node structure.
28 *
29 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
30 * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
31 * @version $Id: NodeUtils.java 463298 2006-10-12 16:10:32Z henning $
32 */
33 public class NodeUtils
34 {
35 /**
36 * Collect all the <SPECIAL_TOKEN>s that
37 * are carried along with a token. Special
38 * tokens do not participate in parsing but
39 * can still trigger certain lexical actions.
40 * In some cases you may want to retrieve these
41 * special tokens, this is simply a way to
42 * extract them.
43 * @param t
44 * @return String with the special tokens.
45 */
46 public static String specialText(Token t)
47 {
48 StringBuffer specialText = new StringBuffer();
49
50 if (t.specialToken == null || t.specialToken.image.startsWith("##") )
51 {
52 return "";
53 }
54
55 Token tmp_t = t.specialToken;
56
57 while (tmp_t.specialToken != null)
58 {
59 tmp_t = tmp_t.specialToken;
60 }
61
62 while (tmp_t != null)
63 {
64 String st = tmp_t.image;
65
66 StringBuffer sb = new StringBuffer();
67
68 for(int i = 0; i < st.length(); i++)
69 {
70 char c = st.charAt(i);
71
72 if ( c == '#' || c == '$' )
73 {
74 sb.append( c );
75 }
76
77 /*
78 * more dreaded MORE hack :)
79 *
80 * looking for ("\\")*"$" sequences
81 */
82
83 if ( c == '\\')
84 {
85 boolean ok = true;
86 boolean term = false;
87
88 int j = i;
89 for( ok = true; ok && j < st.length(); j++)
90 {
91 char cc = st.charAt( j );
92
93 if (cc == '\\')
94 {
95 /*
96 * if we see a \, keep going
97 */
98 continue;
99 }
100 else if( cc == '$' )
101 {
102 /*
103 * a $ ends it correctly
104 */
105 term = true;
106 ok = false;
107 }
108 else
109 {
110 /*
111 * nah...
112 */
113 ok = false;
114 }
115 }
116
117 if (term)
118 {
119 String foo = st.substring( i, j );
120 sb.append( foo );
121 i = j;
122 }
123 }
124 }
125
126 // This is a potential JDK 1.3/JDK 1.4 gotcha. If we remove
127 // the toString() method call, then when compiling under JDK 1.4,
128 // this will be mapped to StringBuffer.append(StringBuffer) and
129 // under JDK 1.3, it will be mapped to StringBuffer.append(Object).
130 // So the JDK 1.4 compiled jar will bomb out under JDK 1.3 with a
131 // MethodNotFound error.
132 //
133 // @todo Once we are JDK 1.4+ only, remove the toString(), make this
134 // loop perform a little bit better.
135 specialText.append(sb.toString());
136
137 tmp_t = tmp_t.next;
138 }
139
140 return specialText.toString();
141 }
142
143 /**
144 * complete node literal
145 * @param t
146 * @return A node literal.
147 *
148 */
149 public static String tokenLiteral( Token t )
150 {
151 return specialText( t ) + t.image;
152 }
153
154 /**
155 * Utility method to interpolate context variables
156 * into string literals. So that the following will
157 * work:
158 *
159 * #set $name = "candy"
160 * $image.getURI("${name}.jpg")
161 *
162 * And the string literal argument will
163 * be transformed into "candy.jpg" before
164 * the method is executed.
165 * @param argStr
166 * @param vars
167 * @return Interpoliation result.
168 * @throws MethodInvocationException
169 */
170 public static String interpolate(String argStr, Context vars) throws MethodInvocationException
171 {
172 StringBuffer argBuf = new StringBuffer();
173
174 for (int cIdx = 0 ; cIdx < argStr.length();)
175 {
176 char ch = argStr.charAt(cIdx);
177
178 switch (ch)
179 {
180 case '$':
181 StringBuffer nameBuf = new StringBuffer();
182 for (++cIdx ; cIdx < argStr.length(); ++cIdx)
183 {
184 ch = argStr.charAt(cIdx);
185 if (ch == '_' || ch == '-'
186 || Character.isLetterOrDigit(ch))
187 nameBuf.append(ch);
188 else if (ch == '{' || ch == '}')
189 continue;
190 else
191 break;
192 }
193
194 if (nameBuf.length() > 0)
195 {
196 Object value = vars.get(nameBuf.toString());
197
198 if (value == null)
199 argBuf.append("$").append(nameBuf.toString());
200 else
201 argBuf.append(value.toString());
202 }
203 break;
204
205 default:
206 argBuf.append(ch);
207 ++cIdx;
208 break;
209 }
210 }
211
212 return argBuf.toString();
213 }
214 }