1 package org.apache.velocity.runtime.directive;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.IOException;
23 import java.io.Writer;
24
25 import org.apache.commons.lang.StringUtils;
26 import org.apache.velocity.context.InternalContextAdapter;
27 import org.apache.velocity.context.ProxyVMContext;
28 import org.apache.velocity.exception.MacroOverflowException;
29 import org.apache.velocity.exception.MethodInvocationException;
30 import org.apache.velocity.exception.TemplateInitException;
31 import org.apache.velocity.exception.VelocityException;
32 import org.apache.velocity.runtime.RuntimeConstants;
33 import org.apache.velocity.runtime.RuntimeServices;
34 import org.apache.velocity.runtime.parser.ParserTreeConstants;
35 import org.apache.velocity.runtime.parser.node.ASTDirective;
36 import org.apache.velocity.runtime.parser.node.Node;
37 import org.apache.velocity.runtime.parser.node.SimpleNode;
38
39
40
41
42
43
44
45
46
47 public class VelocimacroProxy extends Directive
48 {
49 private String macroName;
50 private String[] argArray = null;
51 private String[] literalArgArray = null;
52 private SimpleNode nodeTree = null;
53 private int numMacroArgs = 0;
54 private boolean preInit = false;
55 private boolean strictArguments;
56 private boolean localContextScope = false;
57 private int maxCallDepth;
58
59
60
61
62
63 public String getName()
64 {
65 return macroName;
66 }
67
68
69
70
71
72 public int getType()
73 {
74 return LINE;
75 }
76
77
78
79
80
81
82 public void setName(String name)
83 {
84 macroName = name;
85 }
86
87
88
89
90
91
92 public void setArgArray(String[] arr)
93 {
94 argArray = arr;
95
96
97
98 literalArgArray = new String[arr.length];
99 for(int i = 0; i < arr.length; i++)
100 {
101 literalArgArray[i] = ".literal.$" + argArray[i];
102 }
103
104
105
106
107
108
109 numMacroArgs = argArray.length - 1;
110 }
111
112
113
114
115 public void setNodeTree(SimpleNode tree)
116 {
117 nodeTree = tree;
118 }
119
120
121
122
123
124
125 public int getNumArgs()
126 {
127 return numMacroArgs;
128 }
129
130
131
132
133
134
135
136
137
138
139
140
141 public boolean render(InternalContextAdapter context, Writer writer, Node node)
142 throws IOException, MethodInvocationException, MacroOverflowException
143 {
144
145
146
147 final ProxyVMContext vmc = new ProxyVMContext(context, rsvc, localContextScope);
148
149 int callArguments = node.jjtGetNumChildren();
150
151 if (callArguments > 0)
152 {
153
154 for (int i = 1; i < argArray.length && i <= callArguments; i++)
155 {
156 Node macroCallArgument = node.jjtGetChild(i - 1);
157
158
159
160
161
162
163
164
165
166
167 vmc.addVMProxyArg(context, argArray[i], literalArgArray[i], macroCallArgument);
168 }
169 }
170
171
172
173
174 if (maxCallDepth > 0 && maxCallDepth == vmc.getCurrentMacroCallDepth())
175 {
176 String templateName = vmc.getCurrentTemplateName();
177 Object[] stack = vmc.getMacroNameStack();
178
179 StringBuffer out = new StringBuffer(100)
180 .append("Max calling depth of ").append(maxCallDepth)
181 .append(" was exceeded in Template:").append(templateName)
182 .append(" and Macro:").append(macroName)
183 .append(" with Call Stack:");
184 for (int i = 0; i < stack.length; i++)
185 {
186 if (i != 0)
187 {
188 out.append("->");
189 }
190 out.append(stack[i]);
191 }
192 rsvc.getLog().error(out.toString());
193
194 try
195 {
196 throw new MacroOverflowException(out.toString());
197 }
198 finally
199 {
200
201 while (vmc.getCurrentMacroCallDepth() > 0)
202 {
203 vmc.popCurrentMacroName();
204 }
205 }
206 }
207
208 try
209 {
210
211 vmc.pushCurrentMacroName(macroName);
212 nodeTree.render(vmc, writer);
213 vmc.popCurrentMacroName();
214 return true;
215 }
216 catch (RuntimeException e)
217 {
218 throw e;
219 }
220 catch (Exception e)
221 {
222 String msg = "VelocimacroProxy.render() : exception VM = #" + macroName + "()";
223 rsvc.getLog().error(msg, e);
224 throw new VelocityException(msg, e);
225 }
226 }
227
228
229
230
231
232
233
234
235
236 public void init(RuntimeServices rs, InternalContextAdapter context, Node node)
237 throws TemplateInitException
238 {
239
240 synchronized (this)
241 {
242 if (!preInit)
243 {
244 super.init(rs, context, node);
245
246
247 strictArguments = rs.getConfiguration().getBoolean(
248 RuntimeConstants.VM_ARGUMENTS_STRICT, false);
249
250
251
252 localContextScope = rsvc.getBoolean(RuntimeConstants.VM_CONTEXT_LOCALSCOPE, false);
253
254
255 maxCallDepth = rsvc.getInt(RuntimeConstants.VM_MAX_DEPTH);
256
257
258
259
260 nodeTree.init(context, rs);
261
262 preInit = true;
263 }
264 }
265
266
267 int i = node.jjtGetNumChildren();
268
269
270 if (getNumArgs() != i)
271 {
272
273
274
275
276
277
278
279 for (Node parent = node.jjtGetParent(); parent != null;)
280 {
281 if ((parent instanceof ASTDirective)
282 && StringUtils.equals(((ASTDirective) parent).getDirectiveName(), "macro"))
283 {
284 return;
285 }
286 parent = parent.jjtGetParent();
287 }
288
289 String msg = "VM #" + macroName + ": too "
290 + ((getNumArgs() > i) ? "few" : "many") + " arguments to macro. Wanted "
291 + getNumArgs() + " got " + i;
292
293 if (strictArguments)
294 {
295
296
297
298 throw new TemplateInitException(msg, context.getCurrentTemplateName(), 0, 0);
299 }
300 else
301 {
302 rsvc.getLog().debug(msg);
303 return;
304 }
305 }
306
307
308
309 for (int n=0; n < i; n++)
310 {
311 Node child = node.jjtGetChild(n);
312 if (child.getType() == ParserTreeConstants.JJTWORD)
313 {
314
315
316 throw new TemplateInitException("Invalid arg #"
317 + n + " in VM #" + macroName, context.getCurrentTemplateName(), 0, 0);
318 }
319 }
320 }
321 }
322