I'm using Apache's Velocity templating engine, and I must produce a custom Directive. That's, I wish to have the ability to write "#doMyThing()" and also have it invoke some java code I authored to be able to create the text.

I understand will be able to register a custom directive with the addition of a line

userdirective=my.package.here.MyDirectiveName

to my velocity.qualities file. And That I know will be able to write this type of class by stretching the Directive class. What I'm not sure is how to increase the Directive class -- some kind of documentation for that author of the new Directive. For example Let me determine if my getType() method return "BLOCK" or "LINE" and Let me understand what should my setLocation() method must do?

Can there be any documentation available that's much better than just "Make use of the source, Luke"?

I come up with just a little article about writing custom velocity directives (and tools). Maybe someone will discover it helpful.

Block directives always pay a body and should finish with #finish when utilized in a template. e.g. #foreach( $i in $foo ) it has an appearance! #finish

Line directives don't have an appearance or perhaps an #finish. e.g. #parse( 'foo.vtl' )

You don't have to both with setLocation() whatsoever. The parser uses that.

Every other specifics i can sort out?

Also, have you thought about utilizing a "tool" approach? Even when you do not use VelocityTools to instantly build your tool available and whatnot, you can easily produce a tool class that does what you would like, place it within the context and only possess a method you call to create content otherwise simply have its toString() method create the content. e.g. $tool.doMyThing() or simply $myThing

Directives are perfect for if you want to wreck havoc on Velocity internals (use of InternalContextAdapter or actual Nodes).

Just before velocity v1.6 I'd a #blockset($v)#finish directive to have the ability to cope with a multiline #set($v) but this function has become handled through the #define directive. Custom block directives really are a discomfort with modern IDEs because they do not parse the dwelling properly, presuming your #finish connected with #userBlockDirective is definitely an extra and offers the entire file Red-colored. They must be prevented if at all possible.

I replicated such like in the velocity source code and produced a "blockset" (multiline) directive.

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

}