2008-10-01 28 views
6

Sto utilizzando il motore di templatura Velocity di Apache e vorrei creare una direttiva personalizzata. Cioè, voglio essere in grado di scrivere "#doMyThing()" e farlo invocare un codice java che ho scritto per generare il testo.Come creare una direttiva personalizzata per Apache Velocity

so che posso registrare una direttiva personalizzato con l'aggiunta di una linea

userdirective=my.package.here.MyDirectiveName 

al mio file velocity.properties. E so che posso scrivere una classe del genere estendendo lo Directive class. Quello che non so è come estendere la classe direttiva - una sorta di documentazione per l'autore di una nuova direttiva. Per esempio mi piacerebbe sapere se il mio metodo getType() restituisce "BLOCK" o "LINE" e mi piacerebbe sapere cosa dovrebbe fare il mio metodo setLocation()?

C'è qualche documentazione là fuori che è meglio di "Use the source, Luke"?

risposta

8

Ho messo insieme un po 'di article sulla scrittura di direttive di velocità personalizzate (e strumenti). Forse qualcuno lo troverà utile.

2

Le direttive di blocco accettano sempre un corpo e devono terminare con #end se utilizzato in un modello. per esempio. #foreach ($ i in $ foo) questo ha un corpo! #end

Le direttive di linea non hanno un corpo o un numero. per esempio. #parse ('foo.vtl')

Non è necessario disporre di entrambi con setLocation(). Il parser usa quello.

Eventuali altre specifiche che posso aiutare?

Inoltre, hai considerato l'utilizzo di un approccio "strumento"? Anche se non usi VelocityTools per rendere automaticamente disponibile il tuo strumento e quant'altro, puoi semplicemente creare una classe di strumenti che fa ciò che vuoi, metterlo nel contesto e avere un metodo che chiami per generare contenuti o semplicemente avere il suo Il metodo toString() genera il contenuto. per esempio. $ tool.doMyThing() o solo $ myThing

Le direttive sono le migliori per quando è necessario fare confusione con gli interni di Velocity (accesso a InternalContextAdapter o ai nodi effettivi).

2

Prima di velocity v1.6 avevo una #blockset ($ v) direttiva #end per essere in grado di gestire un #set multilinea ($ v) ma questa funzione è ora gestita dalla direttiva #define. Le direttive di blocco personalizzate sono un problema con gli IDE moderni perché non analizzano correttamente la struttura, supponendo che il #end associato a #userBlockDirective sia un extra e dipinga l'intero file RED. Dovrebbero essere evitati se possibile.

Ho copiato qualcosa di simile dal codice sorgente di velocità e ho creato una direttiva "blockset" (multilinea).

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

Inoltre stava cercando di venire con una direttiva personalizzata. Non è stato possibile trovare alcuna documentazione, quindi ho esaminato alcune direttive create dall'utente: IfNullDirective (semplice e intuitiva), MergeDirective e direttive di build-in di velocità.

Qui è la mia semplice direttiva di blocco che restituisce contenuto compresso (progetto completo con alcune istruzioni per l'installazione di direttiva si trova 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

Sul wiki Velocity, c'è una presentazione e esempio di codice da un discorso ho dato chiamato "Hacking Velocity". Include un esempio di una direttiva personalizzata.

Problemi correlati