如何为 Apache Velocity 创建自定义指令
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/159292/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I create a custom directive for Apache Velocity
提问by mcherm
I am using Apache's Velocity templating engine, and I would like to create a custom Directive. That is, I want to be able to write "#doMyThing()" and have it invoke some java code I wrote in order to generate the text.
我正在使用 Apache 的 Velocity 模板引擎,我想创建一个自定义指令。也就是说,我希望能够编写“#doMyThing()”并让它调用我编写的一些 Java 代码以生成文本。
I know that I can register a custom directive by adding a line
我知道我可以通过添加一行来注册自定义指令
userdirective=my.package.here.MyDirectiveName
to my velocity.properties file. And I know that I can write such a class by extending the Directive class. What I don't know is howto extend the Directive class -- some sort of documentation for the author of a new Directive. For instance I'd like to know if my getType() method return "BLOCK" or "LINE" and I'd like to know what should my setLocation() method should do?
到我的velocity.properties 文件。而且我知道我可以通过扩展Directive 类来编写这样一个类。我不知道如何扩展指令类——新指令作者的某种文档。例如,我想知道我的 getType() 方法是返回“BLOCK”还是“LINE”,我想知道我的 setLocation() 方法应该做什么?
Is there any documentation out there that is better than just "Use the source, Luke"?
有没有比“使用来源,卢克”更好的文档?
回答by serg
回答by serg
Also was trying to come up with a custom directive. Couldn't find any documentation at all, so I looked at some user created directives: IfNullDirective(nice and easy one), MergeDirectiveas well as velocity build-in directives.
还试图提出一个自定义指令。根本找不到任何文档,所以我查看了一些用户创建的指令:IfNullDirective(不错且简单的)、MergeDirective以及速度内置指令。
Here is my simple block directive that returns compressed content (complete project with some directive installation instructions is located here):
这是我返回压缩内容的简单块指令(带有一些指令安装说明的完整项目位于此处):
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import org.apache.velocity.context.InternalContextAdapter;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.TemplateInitException;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.directive.Directive;
import org.apache.velocity.runtime.parser.node.Node;
import org.apache.velocity.runtime.log.Log;
import com.googlecode.htmlcompressor.compressor.HtmlCompressor;
/**
* Velocity directive that compresses an HTML content within #compressHtml ... #end block.
*/
public class HtmlCompressorDirective extends Directive {
private static final HtmlCompressor htmlCompressor = new HtmlCompressor();
private Log log;
public String getName() {
return "compressHtml";
}
public int getType() {
return BLOCK;
}
@Override
public void init(RuntimeServices rs, InternalContextAdapter context, Node node) throws TemplateInitException {
super.init(rs, context, node);
log = rs.getLog();
//set compressor properties
htmlCompressor.setEnabled(rs.getBoolean("userdirective.compressHtml.enabled", true));
htmlCompressor.setRemoveComments(rs.getBoolean("userdirective.compressHtml.removeComments", true));
}
public boolean render(InternalContextAdapter context, Writer writer, Node node)
throws IOException, ResourceNotFoundException, ParseErrorException, MethodInvocationException {
//render content to a variable
StringWriter content = new StringWriter();
node.jjtGetChild(0).render(context, content);
//compress
try {
writer.write(htmlCompressor.compress(content.toString()));
} catch (Exception e) {
writer.write(content.toString());
String msg = "Failed to compress content: "+content.toString();
log.error(msg, e);
throw new RuntimeException(msg, e);
}
return true;
}
}
回答by Will Glass
On the Velocity wiki, there's a presentation and sample code from a talk I gave called "Hacking Velocity". It includes an example of a custom directive.
在 Velocity wiki 上,有一个演示文稿和示例代码,来自我名为“ Hacking Velocity”的演讲。它包括自定义指令的示例。
回答by Nathan Bubna
Block directives always accept a body and must end with #end when used in a template. e.g. #foreach( $i in $foo ) this has a body! #end
块指令总是接受一个主体,并且在模板中使用时必须以 #end 结尾。例如 #foreach( $i in $foo ) 这有一个身体!#结尾
Line directives do not have a body or an #end. e.g. #parse( 'foo.vtl' )
行指令没有正文或#end。例如#parse('foo.vtl')
You don't need to both with setLocation() at all. The parser uses that.
您根本不需要同时使用 setLocation() 。解析器使用它。
Any other specifics i can help with?
我可以帮助其他任何细节吗?
Also, have you considered using a "tool" approach? Even if you don't use VelocityTools to automatically make your tool available and whatnot, you can just create a tool class that does what you want, put it in the context and either have a method you call to generate content or else just have its toString() method generate the content. e.g. $tool.doMyThing() or just $myThing
另外,您是否考虑过使用“工具”方法?即使您不使用 VelocityTools 自动使您的工具可用等等,您也可以只创建一个工具类来执行您想要的操作,将它放在上下文中,或者使用您调用的方法来生成内容,或者只是拥有它的toString() 方法生成内容。例如 $tool.doMyThing() 或只是 $myThing
Directives are best for when you need to mess with Velocity internals (access to InternalContextAdapter or actual Nodes).
当您需要处理 Velocity 内部结构(访问 InternalContextAdapter 或实际节点)时,指令最适合。
回答by gbegley
Prior to velocity v1.6 I had a #blockset($v)#end directive to be able to deal with a multiline #set($v) but this function is now handled by the #define directive. Custom block directives are a pain with modern IDEs because they don't parse the structure correctly, assuming your #end associated with #userBlockDirective is an extra and paints the whole file RED. They should be avoided if possible.
在velocity v1.6 之前,我有一个#blockset($v)#end 指令能够处理多行#set($v) 但这个函数现在由#define 指令处理。自定义块指令对于现代 IDE 来说很痛苦,因为它们不能正确解析结构,假设与 #userBlockDirective 关联的 #end 是额外的,并将整个文件绘制为红色。如果可能,应该避免它们。
I copied something similar from the velocity source code and created a "blockset" (multiline) directive.
我从速度源代码中复制了类似的东西并创建了一个“blockset”(多行)指令。
import org.apache.velocity.runtime.directive.Directive;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.parser.node.Node;
import org.apache.velocity.context.InternalContextAdapter;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.TemplateInitException;
import java.io.Writer;
import java.io.IOException;
import java.io.StringWriter;
public class BlockSetDirective extends Directive {
private String blockKey;
/**
* Return name of this directive.
*/
public String getName() {
return "blockset";
}
/**
* Return type of this directive.
*/
public int getType() {
return BLOCK;
}
/**
* simple init - get the blockKey
*/
public void init( RuntimeServices rs, InternalContextAdapter context,
Node node )
throws TemplateInitException {
super.init( rs, context, node );
/*
* first token is the name of the block. I don't even check the format,
* just assume it looks like this: $block_name. Should check if it has
* a '$' or not like macros.
*/
blockKey = node.jjtGetChild( 0 ).getFirstToken().image.substring( 1 );
}
/**
* Renders node to internal string writer and stores in the context at the
* specified context variable
*/
public boolean render( InternalContextAdapter context, Writer writer,
Node node )
throws IOException, MethodInvocationException,
ResourceNotFoundException, ParseErrorException {
StringWriter sw = new StringWriter(256);
boolean b = node.jjtGetChild( 1 ).render( context, sw );
context.put( blockKey, sw.toString() );
return b;
}
}

