EMMA Coverage Report (generated Tue May 18 22:13:27 CDT 2004)
[all classes][org.apache.velocity.runtime.directive]

COVERAGE SUMMARY FOR SOURCE FILE [VMProxyArg.java]

nameclass, %method, %block, %line, %
VMProxyArg.java100% (1/1)55%  (6/11)52%  (291/558)54%  (62.1/114)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class VMProxyArg100% (1/1)55%  (6/11)52%  (291/558)54%  (62.1/114)
VMProxyArg (VMProxyArg, InternalContextAdapter): void 0%   (0/1)0%   (0/80)0%   (0/24)
getCallerReference (): String 0%   (0/1)0%   (0/3)0%   (0/1)
getNodeTree (): SimpleNode 0%   (0/1)0%   (0/3)0%   (0/1)
getStaticObject (): Object 0%   (0/1)0%   (0/3)0%   (0/1)
getType (): int 0%   (0/1)0%   (0/3)0%   (0/1)
setObject (InternalContextAdapter, Object): Object 100% (1/1)27%  (16/60)36%  (4/11)
getObject (InternalContextAdapter): Object 100% (1/1)52%  (78/149)58%  (18/31)
setup (): void 100% (1/1)67%  (120/180)70%  (21/30)
VMProxyArg (RuntimeServices, String, String, int): void 100% (1/1)100% (71/71)100% (23/23)
getContextReference (): String 100% (1/1)100% (3/3)100% (1/1)
isConstant (): boolean 100% (1/1)100% (3/3)100% (1/1)

1package 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 
19import java.io.StringWriter;
20import java.io.StringReader;
21import java.io.BufferedReader;
22 
23import org.apache.velocity.context.InternalContextAdapter;
24import org.apache.velocity.context.InternalContextAdapterImpl;
25import org.apache.velocity.runtime.RuntimeServices;
26import org.apache.velocity.runtime.parser.node.ASTReference;
27import org.apache.velocity.runtime.parser.ParserTreeConstants;
28import org.apache.velocity.runtime.parser.node.SimpleNode;
29import org.apache.velocity.util.StringUtils;
30 
31import org.apache.velocity.exception.MethodInvocationException;
32import 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 */
78public 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 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 contextReference = null;
97    
98    /** the reference we are proxying for  */
99    private String callerReference = null;
100 
101    /** the 'de-dollared' reference if we are a ref but don't have a method attached */
102    private String 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 contextRef, String 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 setObject(  InternalContextAdapter context,  Object 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 getObject( InternalContextAdapter context )
243    {        
244        try
245        {
246 
247            /*
248             *  we need to output based on our type
249             */
250 
251            Object 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 writer =new StringWriter();
309                    nodeTree.render( context, writer );
310                    
311                    retObject = writer;
312                }
313                catch (Exception 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 buff ="#include(" + callerReference + " ) ";
375 
376                    //ByteArrayInputStream inStream = new ByteArrayInputStream( buff.getBytes() );
377 
378                    BufferedReader br = new BufferedReader( new StringReader( 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 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(true);
422                break;
423            }
424 
425        case ParserTreeConstants.JJTFALSE :
426            {
427                constant = true;
428                staticObject =  new Boolean(false);
429                break;
430            }
431 
432        case ParserTreeConstants.JJTNUMBERLITERAL :
433            {
434                constant = true;
435                staticObject = new Integer(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( 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 getCallerReference()
499    {
500        return callerReference;
501    }
502 
503    public String getContextReference()
504    {
505        return contextReference;
506    }
507 
508    public SimpleNode getNodeTree()
509    {
510        return nodeTree;
511    }
512 
513    public Object getStaticObject()
514    {
515        return staticObject;
516    }
517 
518    public int getType()
519    {
520        return type;
521    }
522}

[all classes][org.apache.velocity.runtime.directive]
EMMA 2.0.4015 (stable) (C) Vladimir Roubtsov