Print
Your own plugin

Create your own plugin.

Why do you need a plugin.

There are a lot of situations where your own xdoclet plugin could be useful. It could generate HTML templates or SWING panels for edition of your java objects, some documentation and a lot of interesting and useful things XDoclet developers haven't thought of yet. Writing a plugin for XDoclet 2 is much easier than for XDoclet 1. Here we show you how to create and use your plugin.

Of course you could use generic template task and be happy with it, but your specific needs may require specific javadoc tags with different parameters or some utlity functions - template engine can sometimes provide them, but with plugin you have full control.

Choose a template engine.

XDoclet-2 provides 3 different template engines.

  • jelly

    Jelly template engine is well suited for XML code generation. It also provides expression language and rich set of taglibs. Most plugins creating XML use jelly templating
  • velocity

    Velocity templating engine is realy lightweight, simple and fast. It's best suited for generating of java sources and other files. You can also generate XML with it.
  • Freemarker

    Fremarker templating engine is alternative to velocity with almost same functions.

Describe javadoc tags.

Stricktly speaking you can use whatever tags you like, and have access to all the fileds and values. But

strict tag declaration has several advantages

  • automatic control of allowed / required values
  • automatic documentation generation
  • automatic tag placement control

Those functions are provided by Qtags plugin which automatically generates supporting classes and tag libraries.

Let's say, you like to create tag @foo.bar with parameters "blurge" "baz" and "bang", which can be used on class level only once. You only need to do following:

"ForBarTag.java"
package foo.bar.baz;

import com.thoughtworks.qdox.model.DocletTag;

/**
 * This tag is just a test tag
 *
 * @qtags.location class
 * @qtags.unique
 * 
 */
public interface FooBarTag extends DocletTag {
    /**
     * Bla bla
     *
     * @qtags.allowed-value red
     * @qtags.allowed-value green
     * @qtags.default green
     */
    String getBlurge();

    /**
     * Ping pong
     *
     * @qtags.required
     */
    String getBaz();

    /**
     * Hip hop
     *
     * @qtags.default true
     */
    boolean isBang();
}

This would produce necessary implementation class for xdoclet tag named @foo.bar. This mplementation would produce error if this tag occures not on class level, occurs there more than once, if you do not specify parameter "baz" or blurge is set to something else than red and green. Default value for blurge would be "green". See QTags tag documentation for available tags.

Once you have created necessary tag interfaces you can process them with qtags plugin to generate real sources:

<goal name="qtags">
        <mkdir dir="${basedir}/target/src"/>
        <path id="xdoclet.generated.path" location="${basedir}/target/src"/>
        <maven:addPath id="maven.compile.src.set" refid="xdoclet.generated.path"/>

        <path id="xdoclet.task.classpath">
            <pathelement location="${basedir}/target/classes"/>
            <path refid="maven.dependency.classpath"/>
        </path>

        <taskdef
            name="xdoclet"
            classname="org.xdoclet.ant.XDocletTask"
            classpathref="xdoclet.task.classpath"
            />

        <xdoclet>
            <fileset dir="${pom.build.sourceDirectory}">
                <include name="**/*.java"/>
            </fileset>

            <component classname="org.generama.VelocityTemplateEngine"/>
            <component classname="org.generama.JellyTemplateEngine"/>
            <component
                classname="org.xdoclet.plugin.qtags.impl.QTagImplPlugin"
                destdir="${basedir}/target/src"
                />
            <component
                classname="org.xdoclet.plugin.qtags.impl.QTagLibraryPlugin"
                destdir="${basedir}/target/src"
                packagereplace="org.xdoclet.plugin.${xdoclet.qtags.namespace}.qtags"
                />
            <component
                classname="org.xdoclet.plugin.qtags.xdoc.QTagXDocPlugin"
                destdir="${basedir}/target/generated-xdocs"
                namespace="${xdoclet.qtags.namespace}"
                />
            <component
                classname="org.xdoclet.plugin.qtags.confluence.QTagConfluencePlugin"
                destdir="${basedir}/target/confluence"
                namespace="${xdoclet.qtags.namespace}"
                />
        </xdoclet>
    </goal>

This example is taken from maven.xml of xdoclet-plugins project , and you see that we use not only qtags plugin. We also generate QTags-Library ( will be used by plugin class ) , documentation in xdoc format, as well as confluence markup ( that's how we generate tag documentation on this site ). Note that we include generated source in javac classpath - of course this shall be called before source compilation.

Writing plugin

XDoclet plugin is a simple thing. Generame provides usefull base classes, like org.generama.defaults.QDoxPlugin or org.generama.defaults.JavaGeneratingPlugin - they can be used as convenient base classes. Here is an example ( real existing xdoclet plugin ):

MessageBundlePlugin:java
package org.xdoclet.plugin.externalizer;

import com.thoughtworks.qdox.model.JavaClass;
import org.generama.defaults.QDoxPlugin;
import org.generama.QDoxCapableMetadataProvider;
import org.generama.VelocityTemplateEngine;
import org.generama.WriterMapper;
import org.xdoclet.plugin.externalizer.qtags.MsgMessageTagImpl;
import org.xdoclet.plugin.externalizer.qtags.TagLibrary;

import java.util.Collection;

public class MessageBundlePlugin extends QDoxPlugin {
    private String lang;
    private String country;
    private String variant;
    

    public MessageBundlePlugin(VelocityTemplateEngine templateEngine, QDoxCapableMetadataProvider metadataProvider,
                               WriterMapper writerMapper) {
        super(templateEngine, metadataProvider, writerMapper);
        setFileregex("\\.java");
        setFilereplace("\\" + getBundleKey() + ".properties");
        setMultioutput(true);
        new TagLibrary(metadataProvider);
        
    }

    public String getLang() {
        return lang;
    }

    public String getCountry() {
        return country;
    }

    public String getVariant() {
        return variant;
    }

    public void setLang(String lang) {
        this.lang = lang;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public void setVariant(String variant) {
        this.variant = variant;
    }

    public String getBundleKey() {
        StringBuffer sb = new StringBuffer();
        if (getLang() != null) {
            sb.append(getLang());
        } else {
            return "";
        }

        if (getCountry() != null) {
            sb.append("_");
            sb.append(getCountry());
        } else {
            return sb.toString();
        }

        if (getVariant() != null) {
            sb.append("_");
            sb.append(getVariant());
        }
        return sb.toString();
    }

    /**
     * decide whether this message tag is applicable  in current context.
     * language country and variant shall match
     */
    public boolean messageApplicable(MsgMessageTagImpl tag) {
        if (getLang() == null) {
            return tag.getLang() == null;
        } else if (getLang().equals(tag.getLang())) {
            if (getCountry() == null) {
                return tag.getCountry() == null;
            } else if (getCountry().equals(tag.getCountry())) {
                if (getVariant() == null) {
                    return tag.getVariant() == null;
                } else {
                    return getVariant().equals(tag.getVariant());
                }
            }
        }
		
        // fallen through
        return false;
    }

    public boolean shouldGenerate(Object metadata) {
        JavaClass clazz = (JavaClass) metadata;
        return clazz.getTagByName("msg.bundle") != null;
    }
}

This plugin derives from qdox plugin, because we do not need specific java generation support.

In constructor it receives template engine ( in this case velocity ) , metadata provider ( QDoxCapableMetadataProvider )

Unit tests

... coming soon

Powered by Atlassian Confluence