KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > velocity > runtime > directive > VelocimacroProxy


1 package org.apache.velocity.runtime.directive;
2
3 /*
4  * Copyright 2000-2001,2004 The Apache Software Foundation.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 import java.io.Writer JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.BufferedReader JavaDoc;
22 import java.io.StringReader JavaDoc;
23
24 import java.util.HashMap JavaDoc;
25
26 import org.apache.velocity.context.InternalContextAdapter;
27 import org.apache.velocity.context.VMContext;
28
29 import org.apache.velocity.runtime.visitor.VMReferenceMungeVisitor;
30 import org.apache.velocity.runtime.RuntimeServices;
31 import org.apache.velocity.runtime.parser.node.Node;
32 import org.apache.velocity.runtime.parser.Token;
33 import org.apache.velocity.runtime.parser.ParserTreeConstants;
34 import org.apache.velocity.runtime.parser.node.SimpleNode;
35 import org.apache.velocity.util.StringUtils;
36
37 import org.apache.velocity.exception.MethodInvocationException;
38
39 /**
40  * VelocimacroProxy.java
41  *
42  * a proxy Directive-derived object to fit with the current directive system
43  *
44  * @author <a HREF="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
45  * @version $Id: VelocimacroProxy.java,v 1.27.4.1 2004/03/03 23:22:56 geirm Exp $
46  */

47 public class VelocimacroProxy extends Directive
48 {
49     private String JavaDoc macroName = "";
50     private String JavaDoc macroBody = "";
51     private String JavaDoc[] argArray = null;
52     private SimpleNode nodeTree = null;
53     private int numMacroArgs = 0;
54     private String JavaDoc namespace = "";
55
56     private boolean init = false;
57     private String JavaDoc[] callingArgs;
58     private int[] callingArgTypes;
59     private HashMap JavaDoc proxyArgHash = new HashMap JavaDoc();
60
61
62     /**
63      * Return name of this Velocimacro.
64      */

65     public String JavaDoc getName()
66     {
67         return macroName;
68     }
69     
70     /**
71      * Velocimacros are always LINE
72      * type directives.
73      */

74     public int getType()
75     {
76         return LINE;
77     }
78  
79     /**
80      * sets the directive name of this VM
81      */

82     public void setName( String JavaDoc name )
83     {
84         macroName = name;
85     }
86    
87     /**
88      * sets the array of arguments specified in the macro definition
89      */

90     public void setArgArray( String JavaDoc [] arr )
91     {
92         argArray = arr;
93
94         /*
95          * get the arg count from the arg array. remember that the arg array
96          * has the macro name as it's 0th element
97          */

98
99         numMacroArgs = argArray.length - 1;
100     }
101
102     public void setNodeTree( SimpleNode tree )
103     {
104         nodeTree = tree;
105     }
106
107     /**
108      * returns the number of ars needed for this VM
109      */

110     public int getNumArgs()
111     {
112         return numMacroArgs;
113     }
114
115     /**
116      * Sets the orignal macro body. This is simply the cat of the macroArray, but the
117      * Macro object creates this once during parsing, and everyone shares it.
118      * Note : it must not be modified.
119      */

120     public void setMacrobody( String JavaDoc mb )
121     {
122         macroBody = mb;
123     }
124
125     public void setNamespace( String JavaDoc ns )
126     {
127         this.namespace = ns;
128     }
129
130     /**
131      * Renders the macro using the context
132      */

133     public boolean render( InternalContextAdapter context, Writer JavaDoc writer, Node node)
134         throws IOException JavaDoc, MethodInvocationException
135     {
136         try
137         {
138             /*
139              * it's possible the tree hasn't been parsed yet, so get
140              * the VMManager to parse and init it
141              */

142        
143             if (nodeTree != null)
144             {
145                 if ( !init )
146                 {
147                     nodeTree.init( context, rsvc);
148                     init = true;
149                 }
150                 
151                 /*
152                  * wrap the current context and add the VMProxyArg objects
153                  */

154
155                 VMContext vmc = new VMContext( context, rsvc );
156
157                 for( int i = 1; i < argArray.length; i++)
158                 {
159                     /*
160                      * we can do this as VMProxyArgs don't change state. They change
161                      * the context.
162                      */

163
164                     VMProxyArg arg = (VMProxyArg) proxyArgHash.get( argArray[i] );
165                     vmc.addVMProxyArg( arg );
166                 }
167          
168                 /*
169                  * now render the VM
170                  */

171
172                 nodeTree.render( vmc, writer );
173             }
174             else
175             {
176                 rsvc.error( "VM error : " + macroName + ". Null AST");
177             }
178         }
179         catch ( Exception JavaDoc e )
180         {
181             /*
182              * if it's a MIE, it came from the render.... throw it...
183              */

184
185             if ( e instanceof MethodInvocationException)
186             {
187                 throw (MethodInvocationException) e;
188             }
189
190             rsvc.error("VelocimacroProxy.render() : exception VM = #" + macroName +
191             "() : " + StringUtils.stackTrace(e));
192         }
193
194         return true;
195     }
196
197     /**
198      * The major meat of VelocimacroProxy, init() checks the # of arguments, patches the
199      * macro body, renders the macro into an AST, and then inits the AST, so it is ready
200      * for quick rendering. Note that this is only AST dependant stuff. Not context.
201      */

202     public void init( RuntimeServices rs, InternalContextAdapter context, Node node)
203        throws Exception JavaDoc
204     {
205         super.init( rs, context, node );
206
207         /*
208          * how many args did we get?
209          */

210        
211         int i = node.jjtGetNumChildren();
212         
213         /*
214          * right number of args?
215          */

216      
217         if ( getNumArgs() != i )
218         {
219             rsvc.error("VM #" + macroName + ": error : too "
220                        + ( (getNumArgs() > i) ? "few" : "many") + " arguments to macro. Wanted "
221                        + getNumArgs() + " got " + i );
222
223             return;
224         }
225
226         /*
227          * get the argument list to the instance use of the VM
228          */

229
230          callingArgs = getArgArray( node );
231        
232         /*
233          * now proxy each arg in the context
234          */

235
236          setupMacro( callingArgs, callingArgTypes );
237          return;
238     }
239
240     /**
241      * basic VM setup. Sets up the proxy args for this
242      * use, and parses the tree
243      */

244     public boolean setupMacro( String JavaDoc[] callArgs, int[] callArgTypes )
245     {
246         setupProxyArgs( callArgs, callArgTypes );
247         parseTree( callArgs );
248
249         return true;
250     }
251
252     /**
253      * parses the macro. We need to do this here, at init time, or else
254      * the local-scope template feature is hard to get to work :)
255      */

256     private void parseTree( String JavaDoc[] callArgs )
257     {
258         try
259         {
260             BufferedReader JavaDoc br = new BufferedReader JavaDoc( new StringReader JavaDoc( macroBody ) );
261
262             /*
263              * now parse the macro - and don't dump the namespace
264              */

265
266             nodeTree = rsvc.parse( br, namespace, false );
267
268             /*
269              * now, to make null references render as proper schmoo
270              * we need to tweak the tree and change the literal of
271              * the appropriate references
272              *
273              * we only do this at init time, so it's the overhead
274              * is irrelevant
275              */

276
277             HashMap JavaDoc hm = new HashMap JavaDoc();
278
279             for( int i = 1; i < argArray.length; i++)
280             {
281                 String JavaDoc arg = callArgs[i-1];
282
283                 /*
284                  * if the calling arg is indeed a reference
285                  * then we add to the map. We ignore other
286                  * stuff
287                  */

288
289                 if (arg.charAt(0) == '$')
290                 {
291                     hm.put( argArray[i], arg );
292                 }
293             }
294
295             /*
296              * now make one of our reference-munging visitor, and
297              * let 'er rip
298              */

299
300             VMReferenceMungeVisitor v = new VMReferenceMungeVisitor( hm );
301             nodeTree.jjtAccept( v, null );
302         }
303         catch ( Exception JavaDoc e )
304         {
305             rsvc.error("VelocimacroManager.parseTree() : exception " + macroName +
306                           " : " + StringUtils.stackTrace(e));
307         }
308     }
309   
310     private void setupProxyArgs( String JavaDoc[] callArgs, int [] callArgTypes )
311     {
312         /*
313          * for each of the args, make a ProxyArg
314          */

315
316         for( int i = 1; i < argArray.length; i++)
317         {
318             VMProxyArg arg = new VMProxyArg( rsvc, argArray[i], callArgs[i-1], callArgTypes[i-1] );
319             proxyArgHash.put( argArray[i], arg );
320         }
321     }
322   
323     /**
324      * gets the args to the VM from the instance-use AST
325      */

326     private String JavaDoc[] getArgArray( Node node )
327     {
328         int numArgs = node.jjtGetNumChildren();
329         
330         String JavaDoc args[] = new String JavaDoc[ numArgs ];
331         callingArgTypes = new int[numArgs];
332
333         /*
334          * eat the args
335          */

336         int i = 0;
337         Token t = null;
338         Token tLast = null;
339     
340         while( i < numArgs )
341         {
342             args[i] = "";
343             /*
344              * we want string literalss to lose the quotes. #foo( "blargh" ) should have 'blargh' patched
345              * into macro body. So for each arg in the use-instance, treat the stringlierals specially...
346              */

347
348             callingArgTypes[i] = node.jjtGetChild(i).getType();
349  
350            
351             if (false && node.jjtGetChild(i).getType() == ParserTreeConstants.JJTSTRINGLITERAL )
352             {
353                 args[i] += node.jjtGetChild(i).getFirstToken().image.substring(1, node.jjtGetChild(i).getFirstToken().image.length() - 1);
354             }
355             else
356             {
357                 /*
358                  * just wander down the token list, concatenating everything together
359                  */

360                 t = node.jjtGetChild(i).getFirstToken();
361                 tLast = node.jjtGetChild(i).getLastToken();
362
363                 while( t != tLast )
364                 {
365                     args[i] += t.image;
366                     t = t.next;
367                 }
368
369                 /*
370                  * don't forget the last one... :)
371                  */

372                 args[i] += t.image;
373             }
374             i++;
375          }
376         return args;
377     }
378 }
379
380
381
382
383
384
385
386
387
388
389
Popular Tags