📄 vmproxyarg.java
字号:
package org.apache.velocity.runtime.directive;
/*
* Copyright 2000-2001,2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.StringWriter;
import java.io.StringReader;
import java.io.BufferedReader;
import org.apache.velocity.context.InternalContextAdapter;
import org.apache.velocity.context.InternalContextAdapterImpl;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.parser.node.ASTReference;
import org.apache.velocity.runtime.parser.ParserTreeConstants;
import org.apache.velocity.runtime.parser.node.SimpleNode;
import org.apache.velocity.util.StringUtils;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.VelocityContext;
/**
* 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 :
* <ul>
* <li> Reference() : anything that starts with '$'
* <li> StringLiteral() : something like "$foo" or "hello geir"
* <li> NumberLiteral() : 1, 2 etc
* <li> IntegerRange() : [ 1..2] or [$foo .. $bar]
* <li> ObjectArray() : [ "a", "b", "c"]
* <li> True() : true
* <li> False() : false
* <li>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
* </ul>
* Now, Reference(), StringLit, NumberLit, 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.</a>
* @version $Id: VMProxyArg.java,v 1.13.4.1 2004/03/03 23:22:56 geirm Exp $
*/
public class VMProxyArg
{
/** type of arg I will have */
private int type = 0;
/** the AST if the type is such that it's dynamic (ex. JJTREFERENCE ) */
private SimpleNode nodeTree = null;
/** reference for the object if we proxy for a static arg like an NumberLiteral */
private Object staticObject = null;
/** not used in this impl : carries the appropriate user context */
private InternalContextAdapter usercontext = null;
/** number of children in our tree if a reference */
private int numTreeChildren = 0;
/** our identity in the current context */
private String contextReference = null;
/** the reference we are proxying for */
private String callerReference = null;
/** the 'de-dollared' reference if we are a ref but don't have a method attached */
private String singleLevelRef = null;
/** by default, we are dynamic. safest */
private boolean constant = false;
/** in the event our type is switched - we don't care really what it is */
private final int GENERALSTATIC = -1;
private RuntimeServices rsvc = null;
/**
* ctor for current impl
*
* takes the reference literal we are proxying for, the literal
* the VM we are for is called with...
*
* @param contextRef reference arg in the definition of the VM, used in the VM
* @param callerRef reference used by the caller as an arg to the VM
* @param t type of arg : JJTREFERENCE, JJTTRUE, etc
*/
public VMProxyArg( RuntimeServices rs, String contextRef, String callerRef, int t )
{
rsvc = rs;
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();
}
}
}
/**
* tells if arg we are poxying for is
* dynamic or constant.
*
* @return true of constant, false otherwise
*/
public boolean isConstant()
{
return constant;
}
/**
* Invoked by VMContext when Context.put() is called for a proxied reference.
*
* @param context context to modify via direct placement, or AST.setValue()
* @param o new value of reference
* @return Object currently null
*/
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 )
{
rsvc.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;
rsvc.error("VMProxyArg.setObject() : Programmer error : I am a constant! No setting! : "
+ contextReference + " / " + callerReference);
}
return null;
}
/**
* 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
*
* @param context Context to use for getting current value
* @return Object value
*
*/
public Object getObject( InternalContextAdapter context )
{
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?
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -