You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@velocity.apache.org by jv...@locus.apache.org on 2000/08/26 08:28:48 UTC
cvs commit: jakarta-velocity/src/java/org/apache/velocity/visitor CacheVisitor.java ProcessVisitor.java
jvanzyl 00/08/25 23:28:48
Modified: src/java/org/apache/velocity/visitor ProcessVisitor.java
Added: src/java/org/apache/velocity/visitor CacheVisitor.java
Log:
- starting building a visitor that will produce a cachable template.
Revision Changes Path
1.2 +0 -1 jakarta-velocity/src/java/org/apache/velocity/visitor/ProcessVisitor.java
Index: ProcessVisitor.java
===================================================================
RCS file: /home/cvs/jakarta-velocity/src/java/org/apache/velocity/visitor/ProcessVisitor.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- ProcessVisitor.java 2000/08/24 21:42:49 1.1
+++ ProcessVisitor.java 2000/08/26 06:28:47 1.2
@@ -62,7 +62,6 @@
import java.util.ArrayList;
import org.apache.velocity.Context;
-import org.apache.velocity.Utils;
import org.apache.velocity.parser.*;
1.1 jakarta-velocity/src/java/org/apache/velocity/visitor/CacheVisitor.java
Index: CacheVisitor.java
===================================================================
package org.apache.velocity.visitor;
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import java.util.Collection;
import java.util.Iterator;
import java.util.Vector;
import java.util.Enumeration;
import java.util.Stack;
import java.util.ArrayList;
import org.apache.velocity.Context;
import org.apache.velocity.parser.*;
/**
* This class is used to build a cachable template.
*/
public class CacheVisitor implements ParserVisitor
{
protected StringBuffer document;
protected int line;
protected int lastLine;
protected int column;
protected Context context;
protected Stack contexts;
protected boolean processingStatement;
protected int foreachBlockLength;
protected int ifBlockLength;
/**
* This is to make adjustments to current line number
* of the resultant document. This is only altered
* by a #foreach directive. But might later be
* used by other looping constructs such as
* #while().
*/
protected int lineAdjustment;
public CacheVisitor()
{
document = new StringBuffer();
line = 1;
lastLine = 0;
column = 1;
lineAdjustment = 0;
foreachBlockLength = 0;
ifBlockLength = 0;
processingStatement = false;
}
/**
* Set the visitor's initial context.
*/
public void setContext(Context context)
{
this.context = context;
}
/**
* The the completed template text.
*/
public String getDocument()
{
return document.toString();
}
/** */
private void print(String text, int x, int y)
{
print(text, x, y, 0);
}
/** */
private void print(String text, int x, int y, int columnAdjustment)
{
for (int i = line; line < (y + lineAdjustment); i++)
{
document.append("\n");
line++;
}
if (line != lastLine)
column = 1;
for (int j = column; column < x; j++)
{
document.append(" ");
column++;
}
document.append(text);
column += text.length() + columnAdjustment;
lastLine = line;
}
/** */
private String location(Token t)
{
return " (" + t.beginColumn + "," + t.beginLine + ")";
}
/** */
public Object visit(SimpleNode node, Object data)
{
data = node.childrenAccept(this, data);
return data;
}
/** */
// use getPropertyValue()
public Object visit(ASTProperty node, Object data)
{
Token t = node.getFirstToken();
String contextKey = t.image.substring(1, t.image.indexOf("."));
String property = "get" + t.image.substring(t.image.indexOf(".") + 1);
if (context.containsKey(contextKey))
{
Object o = context.get(contextKey);
String content = (String) Utils.invoke(o, property);
print(content , t.beginColumn, t.beginLine, t.image.length() - content.length());
}
else
print(t.toString(), t.beginColumn, t.beginLine);
data = node.childrenAccept(this, data);
return data;
}
public Object visit(ASTParameter node, Object data)
{
data = node.childrenAccept(this, data);
return data;
}
public Object visit(ASTParameters node, Object data)
{
data = node.childrenAccept(this, data);
return data;
}
/** */
// use getMethodValue()
public Object visit(ASTMethod node, Object data)
{
Token t = node.getFirstToken();
String contextKey = t.image.substring(1, t.image.indexOf("."));
String property = t.image.substring(t.image.indexOf(".") + 1, t.image.indexOf("("));
if (context.containsKey(contextKey))
{
Object o = context.get(contextKey);
String content = (String) Utils.invoke(o, property);
if (content == null)
content = t.image;
print(content , t.beginColumn, t.beginLine, t.image.length() - content.length());
}
else
print(t.toString(), t.beginColumn, t.beginLine);
data = node.childrenAccept(this, data);
return data;
}
/** */
public Object visit(ASTprocess node, Object data)
{
data = node.childrenAccept(this, data);
return data;
}
/** */
// use getVariableValue()
public Object visit(ASTVariable node, Object data)
{
Token t = node.getFirstToken();
// This should be replaced with getVariableValue!
String variable = t.image.substring(1);
if (context.containsKey(variable))
{
String content = (String) context.get(variable);
print(content , t.beginColumn, t.beginLine, t.image.length() - content.length());
}
else
print(t.toString(), t.beginColumn, t.beginLine);
data = node.childrenAccept(this, data);
return data;
}
/** */
public Object visit(ASTExpression node, Object data)
{
data = node.childrenAccept(this, data);
return data;
}
/** */
public Object visit(ASTBlock node, Object data)
{
Token t1 = node.getFirstToken();
Token t2 = node.getLastToken();
Token p = node.jjtGetParent().getFirstToken();
switch (p.kind)
{
case ParserConstants.FOREACH_DIRECTIVE:
foreachBlockLength = t2.beginLine - t1.beginLine - 1;
data = node.childrenAccept(this, data);
break;
case ParserConstants.ELSE_DIRECTIVE:
case ParserConstants.IF_DIRECTIVE:
// Get rid of the space the {} take up.
lineAdjustment -= 2;
data = node.childrenAccept(this, data);
break;
// This will print braces when not in the context
// of a directive.
default:
print(t1.image, t1.beginColumn, t1.beginLine);
data = node.childrenAccept(this, data);
print(t2.image, t2.beginColumn, t2.beginLine);
}
return data;
}
/** */
// For variables should they just have to be present
// in the context, or should they evaluate to a Boolean
// object.
public Object visit(ASTIfStatement node, Object data)
{
// token 3
boolean expressionState = false;
Token expression = node.getFirstToken().next.next;
switch(expression.kind)
{
case ParserConstants.VARIABLE:
// We might want to change this to evaluate
// the variable to a boolean, not just test for
// existence in the context. Maybe I could test
// for both.
if (getVariableValue(expression.image) != null)
expressionState = true;
break;
case ParserConstants.PROPERTY:
if (((Boolean) getPropertyValue(expression.image)).booleanValue() == true)
expressionState = true;
break;
case ParserConstants.METHOD:
if (((Boolean) getMethodValue(expression.image)).booleanValue() == true)
expressionState = true;
break;
case ParserConstants.TRUE:
expressionState = true;
break;
case ParserConstants.FALSE:
}
// Remove the space that the actual directive
// takes up so that it takes up no space in the
// resultant document.
lineAdjustment--;
// Only process the child nodes if the expression
// evaluates to true. But we also need to look for
// the presence of an else block and process that
// if the expression doesn't evaluate to true.
if (expressionState)
{
processingStatement = true;
data = node.childrenAccept(this, data);
processingStatement = false;
}
else if (node.jjtGetNumChildren() == 2)
{
// We are not going to process the #if block
// but we need to know how much space it
// occupied so that we can remove it. We'll
// remove the space the actual #if directive
// took up here as well.
lineAdjustment -= (
node.jjtGetChild(1).getLastToken().beginLine -
node.jjtGetChild(1).getFirstToken().beginLine - 2);
// Remove the space that the actual directive
// takes up so that it takes up no space in the
// resultant document and remove the space
// the failed #if block above took up.
//lineAdjustment--;
// Then there is another node which is an
// ASTBlock and it is the optional
// #else block to the #if directive above.
// So we want to bypass the first block
// which is only executed #if (true) and
// only process the #else block. We do that
// by grabbing the second child node and process
// that node by passing it to childrenAccept().
// The index is zero-based for jjtGetChild(index).
processingStatement = true;
data = ((SimpleNode) node.jjtGetChild(1)).childrenAccept(this, data);
processingStatement = false;
}
return data;
}
/**
* In a statement like:
*
* 1 2 3 4
* #foreach $knob in $genePool
* {
* }
*
* we want the element ($knob) and
* we want the list ($genePool) from
* which the element is taken.
*
* So we want token 2 and 4.
*/
public Object visit(ASTForeachStatement node, Object data)
{
// tokens 2,4
Object listObject = null;
Token element = node.getFirstToken().next;
Token list = element.next.next;
// Now we have to iterate over all the child nodes
// for each in the list, we have to change the
// context each time the element changes.
// Now I have to adjust the position of the
// tokens.
switch(list.kind)
{
case ParserConstants.VARIABLE:
listObject = getVariableValue(list.image);
break;
case ParserConstants.PROPERTY:
listObject = getPropertyValue(list.image);
break;
case ParserConstants.METHOD:
listObject = getMethodValue(list.image);
break;
}
String elementKey = element.image.substring(1);
// Remove the space that the actual directive
// takes up so that it takes up no space in the
// resultant document and the {.
lineAdjustment -= 2;
// if there is an object in the context with
// the same name as the $element save it so
// we can restore it after the #foreach.
// The Collection interface provides iterator()
// I don't think elements() is provided by an interface
// it just looks like it belongs to Vector. Crappy
// 1.1 vs 1.2 junk.
Object tmp = null;
if (context.containsKey(elementKey))
tmp = context.get(elementKey);
if (listObject instanceof Object[])
{
int length = ((Object[]) listObject).length;
for (int i = 0; i < length; i++)
{
context.put(elementKey,((Object[])listObject)[i]);
processingStatement = true;
data = node.childrenAccept(this, data);
if (i != length - 1)
lineAdjustment += foreachBlockLength;
processingStatement = false;
}
context.remove(elementKey);
}
else if (Utils.implementsMethod(listObject, "iterator"))
{
// Maybe this could be optimized with get(index) ?
// Check the interface. size() and get(index) might
// be faster then using an Iterator.
Iterator i = ((Collection) listObject).iterator();
while (i.hasNext())
{
context.put(elementKey,i.next());
processingStatement = true;
data = node.childrenAccept(this, data);
lineAdjustment += foreachBlockLength;
processingStatement = false;
}
context.remove(elementKey);
}
else if (Utils.implementsMethod(listObject, "elements"))
{
Enumeration e = ((Vector) listObject).elements();
while (e.hasMoreElements())
{
context.put(elementKey,e.nextElement());
processingStatement = true;
data = node.childrenAccept(this, data);
lineAdjustment += foreachBlockLength;
processingStatement = false;
}
context.remove(elementKey);
}
if (tmp != null)
context.put(elementKey, tmp);
// For the final }.
lineAdjustment--;
return data;
}
/** */
public Object visit(ASTSetStatement node, Object data)
{
// tokens 2,4
Object value = null;;
Token leftHand = node.getFirstToken().next;
Token rightHand = leftHand.next.next;
switch(rightHand.kind)
{
case ParserConstants.STRING_LITERAL:
value = getStringLiteralValue(rightHand.image);
break;
case ParserConstants.VARIABLE:
value = getVariableValue(rightHand.image);
break;
case ParserConstants.PROPERTY:
value = getPropertyValue(rightHand.image);
break;
case ParserConstants.METHOD:
value = getMethodValue(rightHand.image);
break;
case ParserConstants.TRUE:
value = new Boolean(true);
break;
case ParserConstants.FALSE:
value = new Boolean(false);
break;
}
switch(leftHand.kind)
{
// We are just setting a variable in the
// context.
case ParserConstants.VARIABLE:
context.put(leftHand.image.substring(1), value);
break;
// We are setting the value of a property
// via a setter.
case ParserConstants.PROPERTY:
setPropertyValue(leftHand.image, value);
break;
}
// Remove the space that the actual directive
// takes up so that it takes up no space in the
// resultant document.
lineAdjustment--;
data = node.childrenAccept(this, data);
return data;
}
/** */
// I don't think this is going to be required given that
// any text can pass through unscathed.
public Object visit(ASTUseStatement node, Object data)
{
data = node.childrenAccept(this, data);
return data;
}
/** */
public Object visit(ASTParamStatement node, Object data)
{
// tokens 2,4
Token name = node.getFirstToken().next;
Token value = name.next.next;
context.putParam(name.image.substring(1), value.image.substring(1, value.image.length() - 1));
// Remove the space that the actual directive
// takes up so that it takes up no space in the
// resultant document.
lineAdjustment--;
data = node.childrenAccept(this, data);
return data;
}
public Object visit(ASTText node, Object data)
{
Token text = node.getFirstToken();
print(text.toString(), text.beginColumn, text.beginLine);
data = node.childrenAccept(this, data);
return data;
}
public Object getStringLiteralValue(String s)
{
return s.substring(1, s.length() - 1);
}
public Object getVariableValue(String s)
{
String variable = s.substring(1);
if (context.containsKey(variable))
{
return context.get(variable);
}
else
{
return null;
}
}
public Object getPropertyValue(String s)
{
String contextKey = s.substring(1, s.indexOf("."));
String property = "get" + s.substring(s.indexOf(".") + 1);
if (context.containsKey(contextKey))
{
Object o = context.get(contextKey);
return Utils.invoke(o, property);
}
else
{
return null;
}
}
// Might have to do some casting here to get
// things right.
public void setPropertyValue(String s, Object value)
{
Object[] args = new Object[1];
String contextKey = s.substring(1, s.indexOf("."));
String property = "set" + s.substring(s.indexOf(".") + 1);
args[0] = value;
if (context.containsKey(contextKey))
{
Object o = context.get(contextKey);
Utils.invoke(o, property, args);
}
// Add some logging if the property doesn't
// exist.
}
public Object getMethodValue(String s)
{
String contextKey = s.substring(1, s.indexOf("."));
String property = s.substring(s.indexOf(".") + 1, s.indexOf("("));
if (context.containsKey(contextKey))
{
Object o = context.get(contextKey);
Object content = Utils.invoke(o, property);
if (content == null)
content = "";
return content;
}
else
{
return "";
}
}
}