2008-10-01 30 views
6

Estoy utilizando el motor de plantillas Velocity de Apache, y me gustaría crear una directiva personalizada. Es decir, quiero poder escribir "#doMyThing()" y hacer que invoque un código Java que escribí para generar el texto.¿Cómo puedo crear una directiva personalizada para Apache Velocity?

sé que puedo registrar una directiva personalizada mediante la adición de una línea

userdirective=my.package.here.MyDirectiveName 

a mi archivo velocity.properties. Y sé que puedo escribir dicha clase extendiendo el Directive class. Lo que no sé es cómo para ampliar la clase directiva - algún tipo de documentación para el autor de una nueva directiva. Por ejemplo, me gustaría saber si mi método getType() devuelve "BLOCK" o "LINE" y me gustaría saber qué debería hacer mi método setLocation().

¿Hay alguna documentación que sea mejor que solo "Use the source, Luke"?

Respuesta

8

Escribí un pequeño article sobre cómo escribir directivas de velocidad (y herramientas) personalizadas. Quizás alguien lo encuentre útil.

2

Las directivas de bloque siempre aceptan un cuerpo y deben terminar con #end cuando se usan en una plantilla. p.ej. #foreach ($ i en $ foo) ¡esto tiene un cuerpo! #end

Las directivas de línea no tienen cuerpo o #end. p.ej. #parse ('foo.vtl')

No necesita ambos con setLocation() en absoluto. El analizador usa eso.

¿Alguna otra información específica que puedo ayudar?

Además, ¿ha considerado usar un enfoque de "herramienta"? Incluso si no usa VelocityTools para hacer que su herramienta esté disponible y demás, puede crear una clase de herramienta que haga lo que desee, ponerla en el contexto y tener un método al que llamar para generar contenido o simplemente tener su El método toString() genera el contenido. p.ej. $ tool.doMyThing() o solo $ myThing

Las directivas son las mejores para cuando necesite meterse con los internos de Velocity (acceso a InternalContextAdapter o nodos reales).

2

Antes de velocity v1.6 Tenía una directiva #blockset ($ v) #end para poder tratar con un conjunto # multilínea ($ v) pero esta función ahora la maneja la directiva #define. Las directivas de bloque personalizado son un problema con los IDE modernos porque no analizan correctamente la estructura, suponiendo que su #end asociado con #userBlockDirective es un extra y pinta todo el archivo ROJO. Deben evitarse si es posible.

Copié algo similar del código fuente de velocidad y creé una directiva "Bloque de bloques" (multilínea).

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; 
    } 

} 
4

También estaba tratando de crear una directiva personalizada. No pude encontrar ninguna documentación en absoluto, así que miré algunas directivas creadas por el usuario: IfNullDirective (buena y fácil), MergeDirective, así como las directivas de construcción de velocidad.

Aquí es mi sencilla Directiva bloque que devuelve el contenido comprimido (proyecto completo con algunas instrucciones de instalación directiva se encuentra 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; 

    } 

} 
4

de la velocidad de la wiki, hay un código de presentación y muestra de una charla que di llamado "Hacking Velocity". Incluye un ejemplo de una directiva personalizada.

Cuestiones relacionadas