KickJava   Java API By Example, From Geeks To Geeks.

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


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.StringWriter JavaDoc;
20 import java.io.StringReader JavaDoc;
21 import java.io.BufferedReader JavaDoc;
22
23 import org.apache.velocity.context.InternalContextAdapter;
24 import org.apache.velocity.context.InternalContextAdapterImpl;
25 import org.apache.velocity.runtime.RuntimeServices;
26 import org.apache.velocity.runtime.parser.node.ASTReference;
27 import org.apache.velocity.runtime.parser.ParserTreeConstants;
28 import org.apache.velocity.runtime.parser.node.SimpleNode;
29 import org.apache.velocity.util.StringUtils;
30
31 import org.apache.velocity.exception.MethodInvocationException;
32 import org.apache.velocity.VelocityContext;
33
34 /**
35  * The function of this class is to proxy for the calling parameter to the VM.
36  *
37  * This class is designed to be used in conjunction with the VMContext class
38  * which knows how to get and set values via it, rather than a simple get()
39  * or put() from a hashtable-like object.
40  *
41  * There is probably a lot of undocumented subtlty here, so step lightly.
42  *
43  * We rely on the observation that an instance of this object has a constant
44  * state throughout its lifetime as it's bound to the use-instance of a VM.
45  * In other words, it's created by the VelocimacroProxy class, to represent
46  * one of the arguments to a VM in a specific template. Since the template
47  * is fixed (it's a file...), we don't have to worry that the args to the VM
48  * will change. Yes, the VM will be called in other templates, or in other
49  * places on the same template, bit those are different use-instances.
50  *
51  * These arguments can be, in the lingo of
52  * the parser, one of :
53  * <ul>
54  * <li> Reference() : anything that starts with '$'
55  * <li> StringLiteral() : something like "$foo" or "hello geir"
56  * <li> NumberLiteral() : 1, 2 etc
57  * <li> IntegerRange() : [ 1..2] or [$foo .. $bar]
58  * <li> ObjectArray() : [ "a", "b", "c"]
59  * <li> True() : true
60  * <li> False() : false
61  * <li>Word() : not likely - this is simply allowed by the parser so we can have
62  * syntactical sugar like #foreach($a in $b) where 'in' is the Word
63  * </ul>
64  * Now, Reference(), StringLit, NumberLit, IntRange, ObjArr are all dynamic things, so
65  * their value is gotten with the use of a context. The others are constants. The trick
66  * we rely on is that the context rather than this class really represents the
67  * state of the argument. We are simply proxying for the thing, returning the proper value
68  * when asked, and storing the proper value in the appropriate context when asked.
69  *
70  * So, the hope here, so an instance of this can be shared across threads, is to
71  * keep any dynamic stuff out of it, relying on trick of having the appropriate
72  * context handed to us, and when a constant argument, letting VMContext punch that
73  * into a local context.
74  *
75  * @author <a HREF="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
76  * @version $Id: VMProxyArg.java,v 1.13.4.1 2004/03/03 23:22:56 geirm Exp $
77  */

78 public class VMProxyArg
79 {
80     /** type of arg I will have */
81     private int type = 0;
82
83     /** the AST if the type is such that it's dynamic (ex. JJTREFERENCE ) */
84     private SimpleNode nodeTree = null;
85
86     /** reference for the object if we proxy for a static arg like an NumberLiteral */
87     private Object JavaDoc staticObject = null;
88
89     /** not used in this impl : carries the appropriate user context */
90     private InternalContextAdapter usercontext = null;
91
92     /** number of children in our tree if a reference */
93     private int numTreeChildren = 0;
94
95     /** our identity in the current context */
96     private String JavaDoc contextReference = null;
97     
98     /** the reference we are proxying for */
99     private String JavaDoc callerReference = null;
100
101     /** the 'de-dollared' reference if we are a ref but don't have a method attached */
102     private String JavaDoc singleLevelRef = null;
103
104     /** by default, we are dynamic. safest */
105     private boolean constant = false;
106
107     /** in the event our type is switched - we don't care really what it is */
108     private final int GENERALSTATIC = -1;
109
110     private RuntimeServices rsvc = null;
111
112     /**
113      * ctor for current impl
114      *
115      * takes the reference literal we are proxying for, the literal
116      * the VM we are for is called with...
117      *
118      * @param contextRef reference arg in the definition of the VM, used in the VM
119      * @param callerRef reference used by the caller as an arg to the VM
120      * @param t type of arg : JJTREFERENCE, JJTTRUE, etc
121      */

122     public VMProxyArg( RuntimeServices rs, String JavaDoc contextRef, String JavaDoc callerRef, int t )
123     {
124         rsvc = rs;
125
126         contextReference = contextRef;
127         callerReference = callerRef;
128         type = t;
129         
130         /*
131          * make our AST if necessary
132          */

133         setup();
134
135         /*
136          * if we are multi-node tree, then save the size to
137          * avoid fn call overhead
138          */

139         if( nodeTree != null)
140             numTreeChildren = nodeTree.jjtGetNumChildren();
141
142         /*
143          * if we are a reference, and 'scalar' (i.e. $foo )
144          * then get the de-dollared ref so we can
145          * hit our context directly, avoiding the AST
146          */

147         if ( type == ParserTreeConstants.JJTREFERENCE )
148         {
149             if ( numTreeChildren == 0)
150             {
151                 /*
152                  * do this properly and use the Reference node
153                  */

154                  singleLevelRef = ((ASTReference) nodeTree).getRootString();
155             }
156         }
157     }
158
159     /**
160      * tells if arg we are poxying for is
161      * dynamic or constant.
162      *
163      * @return true of constant, false otherwise
164      */

165     public boolean isConstant()
166     {
167         return constant;
168     }
169
170     /**
171      * Invoked by VMContext when Context.put() is called for a proxied reference.
172      *
173      * @param context context to modify via direct placement, or AST.setValue()
174      * @param o new value of reference
175      * @return Object currently null
176      */

177     public Object JavaDoc setObject( InternalContextAdapter context, Object JavaDoc o )
178     {
179         /*
180          * if we are a reference, we could be updating a property
181          */

182
183         if( type == ParserTreeConstants.JJTREFERENCE )
184         {
185             if( numTreeChildren > 0)
186             {
187                 /*
188                  * we are a property, and being updated such as
189                  * #foo( $bar.BangStart)
190                  */

191
192                 try
193                 {
194                     ( (ASTReference) nodeTree).setValue( context, o );
195                 }
196                 catch( MethodInvocationException mie )
197                 {
198                     rsvc.error("VMProxyArg.getObject() : method invocation error setting value : " + mie );
199                 }
200            }
201             else
202             {
203                 /*
204                  * we are a 'single level' reference like $foo, so we can set
205                  * out context directly
206                  */

207
208                 context.put( singleLevelRef, o);
209                
210                 // alternate impl : usercontext.put( singleLevelRef, o);
211
}
212         }
213         else
214         {
215             /*
216              * if we aren't a reference, then we simply switch type,
217              * get a new value, and it doesn't go into the context
218              *
219              * in current impl, this shouldn't happen.
220              */

221
222             type = GENERALSTATIC;
223             staticObject = o;
224
225             rsvc.error("VMProxyArg.setObject() : Programmer error : I am a constant! No setting! : "
226                                + contextReference + " / " + callerReference);
227         }
228
229         return null;
230     }
231
232   
233     /**
234      * returns the value of the reference. Generally, this is only
235      * called for dynamic proxies, as the static ones should have
236      * been stored in the VMContext's localcontext store
237      *
238      * @param context Context to use for getting current value
239      * @return Object value
240      *
241      */

242     public Object JavaDoc getObject( InternalContextAdapter context )
243     {
244         try
245         {
246
247             /*
248              * we need to output based on our type
249              */

250
251             Object JavaDoc retObject = null;
252             
253             if ( type == ParserTreeConstants.JJTREFERENCE )
254             {
255                 /*
256                  * two cases : scalar reference ($foo) or multi-level ($foo.bar....)
257                  */

258                 
259                 if ( numTreeChildren == 0)
260                 {
261                     /*
262                      * if I am a single-level reference, can I not get get it out of my context?
263                      */

264                 
265                     retObject = context.get( singleLevelRef );
266                 }
267                 else
268                 {
269                     /*
270                      * I need to let the AST produce it for me.
271                      */

272
273                     retObject = nodeTree.execute( null, context);
274                 }
275             }
276             else if( type == ParserTreeConstants.JJTOBJECTARRAY )
277             {
278                 retObject = nodeTree.value( context );
279             }
280             else if ( type == ParserTreeConstants.JJTINTEGERRANGE)
281             {
282                 retObject = nodeTree.value( context );
283             }
284             else if( type == ParserTreeConstants.JJTTRUE )
285             {
286                 retObject = staticObject;
287             }
288             else if ( type == ParserTreeConstants.JJTFALSE )
289             {
290                 retObject = staticObject;
291             }
292             else if ( type == ParserTreeConstants.JJTSTRINGLITERAL )
293             {
294                 retObject = nodeTree.value( context );
295             }
296             else if ( type == ParserTreeConstants.JJTNUMBERLITERAL )
297             {
298                 retObject = staticObject;
299             }
300             else if ( type == ParserTreeConstants.JJTTEXT )
301             {
302                 /*
303                  * this really shouldn't happen. text is just a thowaway arg for #foreach()
304                  */

305                            
306                 try
307                 {
308                     StringWriter JavaDoc writer =new StringWriter JavaDoc();
309                     nodeTree.render( context, writer );
310                     
311                     retObject = writer;
312                 }
313                 catch (Exception JavaDoc e )
314                 {
315                     rsvc.error("VMProxyArg.getObject() : error rendering reference : " + e );
316                 }
317             }
318             else if( type == GENERALSTATIC )
319             {
320                 retObject = staticObject;
321             }
322             else
323             {
324                 rsvc.error("Unsupported VM arg type : VM arg = " + callerReference +" type = " + type + "( VMProxyArg.getObject() )");
325             }
326             
327             return retObject;
328         }
329         catch( MethodInvocationException mie )
330         {
331             /*
332              * not ideal, but otherwise we propogate out to the
333              * VMContext, and the Context interface's put/get
334              * don't throw. So this is a the best compromise
335              * I can think of
336              */

337             
338             rsvc.error("VMProxyArg.getObject() : method invocation error getting value : " + mie );
339             
340             return null;
341         }
342     }
343
344     /**
345      * does the housekeeping upon creationg. If a dynamic type
346      * it needs to make an AST for further get()/set() operations
347      * Anything else is constant.
348      */

349     private void setup()
350     {
351         switch( type ) {
352
353         case ParserTreeConstants.JJTINTEGERRANGE :
354         case ParserTreeConstants.JJTREFERENCE :
355         case ParserTreeConstants.JJTOBJECTARRAY :
356         case ParserTreeConstants.JJTSTRINGLITERAL :
357         case ParserTreeConstants.JJTTEXT :
358             {
359                 /*
360                  * dynamic types, just render
361                  */

362                 
363                 constant = false;
364
365                 try
366                 {
367                     /*
368                      * fakie : wrap in directive to get the parser to treat our args as args
369                      * it doesn't matter that #include() can't take all these types, because we
370                      * just want the parser to consider our arg as a Directive/VM arg rather than
371                      * as if inline in schmoo
372                      */

373
374                     String JavaDoc buff ="#include(" + callerReference + " ) ";
375
376                     //ByteArrayInputStream inStream = new ByteArrayInputStream( buff.getBytes() );
377

378                     BufferedReader JavaDoc br = new BufferedReader JavaDoc( new StringReader JavaDoc( buff ) );
379
380                     nodeTree = rsvc.parse(br, "VMProxyArg:" + callerReference, true);
381
382                     /*
383                      * now, our tree really is the first DirectiveArg(), and only one
384                      */

385
386                     nodeTree = (SimpleNode) nodeTree.jjtGetChild(0).jjtGetChild(0);
387
388                     /*
389                      * sanity check
390                      */

391
392                     if ( nodeTree != null && nodeTree.getType() != type )
393                     {
394                         rsvc.error( "VMProxyArg.setup() : programmer error : type doesn't match node type.");
395                     }
396
397                     /*
398                      * init. be a good citizen and give it an ICA
399                      */

400
401                     InternalContextAdapter ica
402                             = new InternalContextAdapterImpl(new VelocityContext());
403
404                     ica.pushCurrentTemplateName("VMProxyArg : "
405                             + ParserTreeConstants.jjtNodeName[type]);
406
407                     nodeTree.init(ica, rsvc);
408                 }
409                 catch ( Exception JavaDoc e )
410                 {
411                     rsvc.error("VMProxyArg.setup() : exception " + callerReference +
412                                   " : " + StringUtils.stackTrace(e));
413                 }
414
415                 break;
416             }
417             
418         case ParserTreeConstants.JJTTRUE :
419             {
420                 constant = true;
421                 staticObject = new Boolean JavaDoc(true);
422                 break;
423             }
424
425         case ParserTreeConstants.JJTFALSE :
426             {
427                 constant = true;
428                 staticObject = new Boolean JavaDoc(false);
429                 break;
430             }
431
432         case ParserTreeConstants.JJTNUMBERLITERAL :
433             {
434                 constant = true;
435                 staticObject = new Integer JavaDoc(callerReference);
436                 break;
437             }
438
439       case ParserTreeConstants.JJTWORD :
440           {
441               /*
442                * this is technically an error...
443                */

444
445               rsvc.error("Unsupported arg type : " + callerReference
446                             + " You most likely intended to call a VM with a string literal, so enclose with ' or \" characters. (VMProxyArg.setup())");
447               constant = true;
448               staticObject = new String JavaDoc( callerReference );
449
450               break;
451           }
452  
453         default :
454             {
455                  rsvc.error(" VMProxyArg.setup() : unsupported type : "
456                                     + callerReference );
457             }
458         }
459     }
460
461     /*
462      * CODE FOR ALTERNATE IMPL : please ignore. I will remove when confortable with current.
463      */

464
465     /**
466      * not used in current impl
467      *
468      * Constructor for alternate impl where VelProxy class would make new
469      * VMProxyArg objects, and use this contructor to avoid reparsing the
470      * reference args
471      *
472      * that impl also had the VMProxyArg carry it's context
473      */

474     public VMProxyArg( VMProxyArg model, InternalContextAdapter c )
475     {
476         usercontext = c;
477         contextReference = model.getContextReference();
478         callerReference = model.getCallerReference();
479         nodeTree = model.getNodeTree();
480         staticObject = model.getStaticObject();
481         type = model.getType();
482
483        if( nodeTree != null)
484             numTreeChildren = nodeTree.jjtGetNumChildren();
485
486         if ( type == ParserTreeConstants.JJTREFERENCE )
487         {
488             if ( numTreeChildren == 0)
489             {
490                 /*
491                  * use the reference node to do this...
492                  */

493                 singleLevelRef = ((ASTReference) nodeTree).getRootString();
494             }
495         }
496     }
497   
498     public String JavaDoc getCallerReference()
499     {
500         return callerReference;
501     }
502
503     public String JavaDoc getContextReference()
504     {
505         return contextReference;
506     }
507
508     public SimpleNode getNodeTree()
509     {
510         return nodeTree;
511     }
512
513     public Object JavaDoc getStaticObject()
514     {
515         return staticObject;
516     }
517
518     public int getType()
519     {
520         return type;
521     }
522 }
523
Popular Tags