org.apache.velocity.runtime.directive
public class: VMProxyArg [javadoc |
source]
java.lang.Object
org.apache.velocity.runtime.directive.VMProxyArg
The function of this class is to proxy for the calling parameter to the VM.
This class is designed to be used in conjunction with the VMContext class
which knows how to get and set values via it, rather than a simple get()
or put() from a hashtable-like object.
There is probably a lot of undocumented subtlty here, so step lightly.
We rely on the observation that an instance of this object has a constant
state throughout its lifetime as it's bound to the use-instance of a VM.
In other words, it's created by the VelocimacroProxy class, to represent
one of the arguments to a VM in a specific template. Since the template
is fixed (it's a file...), we don't have to worry that the args to the VM
will change. Yes, the VM will be called in other templates, or in other
places on the same template, bit those are different use-instances.
These arguments can be, in the lingo of
the parser, one of :
- Reference() : anything that starts with '$'
- StringLiteral() : something like "$foo" or "hello geir"
- IntegerLiteral() : 1, 2 etc
- FloatingPointLiteral() : 1.2, 2e5 etc
- IntegerRange() : [ 1..2] or [$foo .. $bar]
- ObjectArray() : [ "a", "b", "c"]
- True() : true
- False() : false
- Word() : not likely - this is simply allowed by the parser so we can have
syntactical sugar like #foreach($a in $b) where 'in' is the Word
Now, Reference(), StringLit, IntegerLiteral, IntRange, ObjArr are all dynamic things, so
their value is gotten with the use of a context. The others are constants. The trick
we rely on is that the context rather than this class really represents the
state of the argument. We are simply proxying for the thing, returning the proper value
when asked, and storing the proper value in the appropriate context when asked.
So, the hope here, so an instance of this can be shared across threads, is to
keep any dynamic stuff out of it, relying on trick of having the appropriate
context handed to us, and when a constant argument, letting VMContext punch that
into a local context.
- author:
< - a href="mailto:geirm@optonline.net">Geir Magnusson Jr.
- version:
$ - Id: VMProxyArg.java 473363 2006-11-10 15:19:09Z wglass $
| Constructor: |
public VMProxyArg(VMProxyArg model,
InternalContextAdapter c) {
contextReference = model.getContextReference();
callerReference = model.getCallerReference();
nodeTree = model.getNodeTree();
staticObject = model.getStaticObject();
type = model.getType();
if( nodeTree != null)
numTreeChildren = nodeTree.jjtGetNumChildren();
if ( type == ParserTreeConstants.JJTREFERENCE )
{
if ( numTreeChildren == 0)
{
/*
* use the reference node to do this...
*/
singleLevelRef = ((ASTReference) nodeTree).getRootString();
}
}
}
not used in current impl
Constructor for alternate impl where VelProxy class would make new
VMProxyArg objects, and use this contructor to avoid reparsing the
reference args
that impl also had the VMProxyArg carry it's context. |
public VMProxyArg(RuntimeServices rs,
String contextRef,
String callerRef,
int t) {
rsvc = rs;
log = rsvc.getLog();
contextReference = contextRef;
callerReference = callerRef;
type = t;
/*
* make our AST if necessary
*/
setup();
/*
* if we are multi-node tree, then save the size to
* avoid fn call overhead
*/
if( nodeTree != null)
{
numTreeChildren = nodeTree.jjtGetNumChildren();
}
/*
* if we are a reference, and 'scalar' (i.e. $foo )
* then get the de-dollared ref so we can
* hit our context directly, avoiding the AST
*/
if ( type == ParserTreeConstants.JJTREFERENCE )
{
if ( numTreeChildren == 0)
{
/*
* do this properly and use the Reference node
*/
singleLevelRef = ((ASTReference) nodeTree).getRootString();
}
}
}
ctor for current impl
takes the reference literal we are proxying for, the literal
the VM we are for is called with... Parameters:
rs -
contextRef - reference arg in the definition of the VM, used in the VM
callerRef - reference used by the caller as an arg to the VM
t - type of arg : JJTREFERENCE, JJTTRUE, etc
|
| Method from org.apache.velocity.runtime.directive.VMProxyArg Detail: |
public String getCallerReference() {
return callerReference;
}
|
public String getContextReference() {
return contextReference;
}
|
public SimpleNode getNodeTree() {
return nodeTree;
}
|
public Object getObject(InternalContextAdapter context) throws MethodInvocationException {
try
{
/*
* we need to output based on our type
*/
Object retObject = null;
if ( type == ParserTreeConstants.JJTREFERENCE )
{
/*
* two cases : scalar reference ($foo) or multi-level ($foo.bar....)
*/
if ( numTreeChildren == 0)
{
/*
* if I am a single-level reference, can I not get get it out of my context?
*/
retObject = context.get( singleLevelRef );
}
else
{
/*
* I need to let the AST produce it for me.
*/
retObject = nodeTree.execute( null, context);
}
}
else if (type == ParserTreeConstants.JJTMAP)
{
retObject = nodeTree.value(context);
}
else if( type == ParserTreeConstants.JJTOBJECTARRAY )
{
retObject = nodeTree.value( context );
}
else if ( type == ParserTreeConstants.JJTINTEGERRANGE)
{
retObject = nodeTree.value( context );
}
else if( type == ParserTreeConstants.JJTTRUE )
{
retObject = staticObject;
}
else if ( type == ParserTreeConstants.JJTFALSE )
{
retObject = staticObject;
}
else if ( type == ParserTreeConstants.JJTSTRINGLITERAL )
{
retObject = nodeTree.value( context );
}
else if ( type == ParserTreeConstants.JJTINTEGERLITERAL )
{
retObject = staticObject;
}
else if ( type == ParserTreeConstants.JJTFLOATINGPOINTLITERAL )
{
retObject = staticObject;
}
else if ( type == ParserTreeConstants.JJTTEXT )
{
/*
* this really shouldn't happen. text is just a thowaway arg for #foreach()
*/
try
{
StringWriter writer =new StringWriter();
nodeTree.render( context, writer );
retObject = writer;
}
/**
* pass through application level runtime exceptions
*/
catch( RuntimeException e )
{
throw e;
}
catch (Exception e )
{
log.error("VMProxyArg.getObject() : error rendering reference", e);
}
}
else if( type == GENERALSTATIC )
{
retObject = staticObject;
}
else
{
log.error("Unsupported VM arg type : VM arg = " +
callerReference +" type = " + type +
"( VMProxyArg.getObject() )");
}
return retObject;
}
catch( MethodInvocationException mie )
{
/*
* not ideal, but otherwise we propogate out to the
* VMContext, and the Context interface's put/get
* don't throw. So this is a the best compromise
* I can think of
*/
log.error("VMProxyArg.getObject() : method invocation error getting value", mie);
throw mie;
}
}
returns the value of the reference. Generally, this is only
called for dynamic proxies, as the static ones should have
been stored in the VMContext's localcontext store |
public Object getStaticObject() {
return staticObject;
}
|
public int getType() {
return type;
}
|
public boolean isConstant() {
return constant;
}
tells if arg we are poxying for is
dynamic or constant. |
public Object setObject(InternalContextAdapter context,
Object o) {
/*
* if we are a reference, we could be updating a property
*/
if( type == ParserTreeConstants.JJTREFERENCE )
{
if( numTreeChildren > 0)
{
/*
* we are a property, and being updated such as
* #foo( $bar.BangStart)
*/
try
{
( (ASTReference) nodeTree).setValue( context, o );
}
catch( MethodInvocationException mie )
{
log.error("VMProxyArg.getObject() : method invocation error setting value", mie);
}
}
else
{
/*
* we are a 'single level' reference like $foo, so we can set
* out context directly
*/
context.put( singleLevelRef, o);
// alternate impl : usercontext.put( singleLevelRef, o);
}
}
else
{
/*
* if we aren't a reference, then we simply switch type,
* get a new value, and it doesn't go into the context
*
* in current impl, this shouldn't happen.
*/
type = GENERALSTATIC;
staticObject = o;
log.error("VMProxyArg.setObject() : Programmer error : I am a constant! No setting! : "
+ contextReference + " / " + callerReference);
}
return null;
}
Invoked by VMContext when Context.put() is called for a proxied reference. |