Я использую механизм шаблонов Apache Velocity и хочу создать специальную директиву. То есть я хочу иметь возможность написать «#doMyThing ()» и заставить его вызывать некоторый написанный мной код Java для генерации текста.
Я знаю, что могу зарегистрировать настраиваемую директиву, добавив строку
userdirective=my.package.here.MyDirectiveName
в мой файл velocity.properties. И я знаю, что могу написать такой класс, расширив Директива класс. Чего я не знаю, так это как для расширения класса Directive - своего рода документации для автора новой директивы. Например, я хотел бы знать, возвращает ли мой метод getType () «BLOCK» или «LINE», и я хотел бы знать, что должен делать мой метод setLocation ()?
Есть ли какая-нибудь документация лучше, чем просто "Используйте источник, Люк"?

Директивы блока всегда принимают тело и должны заканчиваться на #end при использовании в шаблоне. например #foreach ($ i in $ foo) у этого есть тело! #конец
Директивы Line не имеют тела или #end. например #parse ('foo.vtl')
С setLocation () вам вообще не нужно делать то и другое одновременно. Парсер использует это.
Есть ли другие особенности, с которыми я могу помочь?
Кроме того, рассматривали ли вы использование «инструментального» подхода? Даже если вы не используете VelocityTools для автоматического предоставления доступа к вашему инструменту и тому подобному, вы можете просто создать класс инструмента, который делает то, что вы хотите, поместить его в контекст и либо иметь метод, который вы вызываете для генерации контента, либо просто иметь его toString () генерирует контент. например $ tool.doMyThing () или просто $ myThing
Директивы лучше всего подходят, когда вам нужно возиться с внутренними компонентами Velocity (доступ к InternalContextAdapter или фактическим узлам).
До скорости 1.6 у меня была директива #blockset ($ v) #end, чтобы иметь возможность работать с многострочным #set ($ v), но теперь эта функция обрабатывается директивой #define. Настраиваемые директивы блоков являются проблемой для современных IDE, потому что они неправильно анализируют структуру, предполагая, что ваш #end, связанный с #userBlockDirective, является дополнительным и окрашивает весь файл в КРАСНЫЙ цвет. По возможности их следует избегать.
Я скопировал нечто подобное из исходного кода скорости и создал директиву «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;
}
}
Также пытался придумать специальную директиву. Не удалось найти никакой документации, поэтому я просмотрел некоторые директивы, созданные пользователем: IfNullDirective (красивый и простой), MergeDirective, а также директивы для встроенной скорости.
Вот моя простая блочная директива, которая возвращает сжатый контент (полный проект с некоторыми инструкциями по установке директив находится здесь):
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;
}
}
На вики-странице Velocity есть презентация и пример кода из моего выступления под названием "Скорость взлома". Он включает пример настраиваемой директивы.
Я собрал немного статья о написании собственных директив скорости (и инструментов). Может кому пригодится.