Как создать специальную директиву для Apache Velocity

Я использую механизм шаблонов Apache Velocity и хочу создать специальную директиву. То есть я хочу иметь возможность написать «#doMyThing ()» и заставить его вызывать некоторый написанный мной код Java для генерации текста.

Я знаю, что могу зарегистрировать настраиваемую директиву, добавив строку

userdirective=my.package.here.MyDirectiveName

в мой файл velocity.properties. И я знаю, что могу написать такой класс, расширив Директива класс. Чего я не знаю, так это как для расширения класса Directive - своего рода документации для автора новой директивы. Например, я хотел бы знать, возвращает ли мой метод getType () «BLOCK» или «LINE», и я хотел бы знать, что должен делать мой метод setLocation ()?

Есть ли какая-нибудь документация лучше, чем просто "Используйте источник, Люк"?

Как установить LAMP Stack 1/2 на Azure Linux VM
Как установить LAMP Stack 1/2 на Azure Linux VM
В дополнение к нашему предыдущему сообщению о намерении Azure прекратить поддержку Azure Database для MySQL в качестве единого сервера после 16...
6
0
7 403
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Директивы блока всегда принимают тело и должны заканчиваться на #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 есть презентация и пример кода из моего выступления под названием "Скорость взлома". Он включает пример настраиваемой директивы.

Ответ принят как подходящий

Я собрал немного статья о написании собственных директив скорости (и инструментов). Может кому пригодится.

Другие вопросы по теме