Rough Book

random musings of just another computer nerd

Tag: custom tags

Packaging and distributing taglibs in a JAR

This is more of a “note to self” than a “how to”.

If you’re trying to distribute tag files in a JAR, you need to put them under /META-INF/tags. You then need to create a TLD file that you also put under /META-INF/tags. If you have tags or functions that you created in Java, and want to distribute them alongside the tag files, you need to reference them in the TLD and package them in the same JAR (goes without saying).

If you want to do the same thing in maven, the location for the tag files and the tld file is different; you need to put them in src/main/resources/META-INF/tags. Then you can run mvn package and maven will create a JAR with your tags.
Read the rest of this entry »

JSTL, instanceof, and hasProperty

I’ve been doing a little bit of JSTL over the past week, especially custom tags. I’ve written custom tags in Grails before, and there you use actual Groovy code. I guess this was how custom tags used to be written (in Java), but now you can can build your own custom tags using the standard tag library. The standard tag library is still pretty useful when it comes to building custom tags. Since it’s not straight Java, it forces you think really hard about your logic. You don’t want to put any business or application logic in your tag, and you want to restrict everything to view or presentation logic. A side effect of it not being Java is that if you want to do anything extremely complicated, you’re probably better off writing the tag in Java (making sure that you don’t let any business logic creep in).

While writing my own custom tag, I noticed that although instanceof is a reserved word in the JSTL EL (expression language), it is not supported as an operator. The reason I wanted to use the instanceof operator is that I have an attribute that could either be a List or a Map and depending on the type, I wanted to do different things.

Another thing I was trying to do, was to inspect the incoming object to see if it had a certain property (reflection). JSTL uses reflection so that you can access the properties of an object via dot notation, if they follow the JavaBean naming-convention. However, there was no way for me to see if an object had a certain property. To solve both these problems, I wrote my own JSTL functions.
Read the rest of this entry »

Creating a custom SELECT tag with OPTGROUP in Grails

I’ve recently started working with Groovy on Grails at work. Groovy is a dynamic language that runs on the JVM (Java Virtual Machine), and Grails is a high-productivity open-source web-application framework that leverages the Groovy language. Grails follows the “convention over configuration” principle, taking away a lot of the grunt work that goes into creating web applications. It also uses proven technology like Hibernate and Spring with a consistent interface. You can have a Grails app up and ready to go with only a few lines of code. This high productivity is also due to the fact that Grails uses Groovy, which is a dynamic language and is much more expressive than Java. Finally, because Groovy runs on the JVM, it has full access to Java’s rich API.

Groovy comes with a number of built-in tags which allow you to quickly create forms, form controls, or any number of other HTML constructs or elements. However, I noticed that Groovy didn’t provide a built-in tag to build a SELECT that includes OPTGROUPs. OPTGROUPs allow you to group options within a drop-down select-box. But this problem can be solved because Groovy supports the creation of custom tags. After reading a bunch of documentation and looking at a few examples, I was able to create my own SELECT tag that supports OPTGROUPs. Here is an example of the usage of the tag:

<g:selectWithOptGroup name = "song.id" 
                      from = "${Song.list()}" 
                      optionKey = "id" 
                      optionValue = "songName" 
                      groupBy = "album" />

And the code to implement this tag:

import org.springframework.beans.SimpleTypeConverter
import org.springframework.web.servlet.support.RequestContextUtils as RCU
import org.codehaus.groovy.grails.commons.DomainClassArtefactHandler

class MyAppTagLib {

    def selectWithOptGroup = {attrs ->
        def messageSource = grailsAttributes.getApplicationContext().getBean("messageSource")
        def locale = RCU.getLocale(request)
        def writer = out
        def from = attrs.remove('from')
        def keys = attrs.remove('keys')
        def optionKey = attrs.remove('optionKey')
        def optionValue = attrs.remove('optionValue')
        def groupBy = attrs.remove('groupBy')
        def value = attrs.remove('value')
        def valueMessagePrefix = attrs.remove('valueMessagePrefix')
        def noSelection = attrs.remove('noSelection')
        def disabled = attrs.remove('disabled')
        Set optGroupSet = new TreeSet();
        attrs.id = attrs.id ? attrs.id : attrs.name

        if (value instanceof Collection && attrs.multiple == null) {
            attrs.multiple = 'multiple'
        }

        if (noSelection != null) {
            noSelection = noSelection.entrySet().iterator().next()
        }

        if (disabled && Boolean.valueOf(disabled)) {
            attrs.disabled = 'disabled'
        }

        // figure out the groups 
        from.each {
            optGroupSet.add(it.properties[groupBy])
        }

        writer << "<select name=\"${attrs.remove('name')}\" "
        // process remaining attributes
        outputAttributes(attrs)
        writer << '>'
        writer.println()

        if (noSelection) {
            renderNoSelectionOption(noSelection.key, noSelection.value, value)
            writer.println()
        }

        // create options from list
        if (from) {
            //iterate through group set
            for(optGroup in optGroupSet) {
                writer << " <optgroup label=\"${optGroup.encodeAsHTML()}\">"
                writer.println()

                from.eachWithIndex {el, i ->
                    if(el.properties[groupBy].equals(optGroup)) {

                        def keyValue = null
                        writer << '<option '

                        if (keys) {
                            keyValue = keys&#91;i&#93;
                            writeValueAndCheckIfSelected(keyValue, value, writer)
                        }

                        else if (optionKey) {
                            if (optionKey instanceof Closure) {
                                keyValue = optionKey(el)
                            }

                            else if (el != null && optionKey == 'id' && grailsApplication.getArtefact(DomainClassArtefactHandler.TYPE, el.getClass().name)) {
                                keyValue = el.ident()
                            }

                            else {
                                keyValue = el&#91;optionKey&#93;
                            }

                            writeValueAndCheckIfSelected(keyValue, value, writer)
                        }

                        else {
                            keyValue = el
                            writeValueAndCheckIfSelected(keyValue, value, writer)
                        }

                        writer << '>'

                        if (optionValue) {
                            if (optionValue instanceof Closure) {
                                writer << optionValue(el).toString().encodeAsHTML()
                            }

                            else {
                                writer << el&#91;optionValue&#93;.toString().encodeAsHTML()
                            }

                        }

                        else if (valueMessagePrefix) {
                            def message = messageSource.getMessage("${valueMessagePrefix}.${keyValue}", null, null, locale)

                            if (message != null) {
                                writer << message.encodeAsHTML()
                            }

                            else if (keyValue) {
                                writer << keyValue.encodeAsHTML()
                            }

                            else {
                                def s = el.toString()
                                if (s) writer << s.encodeAsHTML()
                            }
                        }

                        else {
                            def s = el.toString()
                            if (s) writer << s.encodeAsHTML()
                        }

                        writer << '</option>'
                        writer.println()
                    }
                }

                writer << '</optgroup>'
                writer.println()
            }
        }
        // close tag
        writer << '</select>'
    }

    void outputAttributes(attrs) {
        attrs.remove('tagName') // Just in case one is left
        attrs.each {k, v ->
            out << k << "=\"" << v.encodeAsHTML() << "\" "
        }
    }

    def typeConverter = new SimpleTypeConverter()
    private writeValueAndCheckIfSelected(keyValue, value, writer) {
        boolean selected = false
        def keyClass = keyValue?.getClass()
        if (keyClass.isInstance(value)) {
            selected = (keyValue == value)
        }
        else if (value instanceof Collection) {
            selected = value.contains(keyValue)
        }
        else if (keyClass && value) {
            try {
                value = typeConverter.convertIfNecessary(value, keyClass)
                selected = (keyValue == value)
            } catch (Exception) {
                // ignore
            }
        }
        writer << "value=\"${keyValue}\" "
        if (selected) {
            writer << 'selected="selected" '
        }
    }

    def renderNoSelectionOption = {noSelectionKey, noSelectionValue, value ->
        // If a label for the '--Please choose--' first item is supplied, write it out
        out << '<option value="' << (noSelectionKey == null ? "" : noSelectionKey) << '"'
        if (noSelectionKey.equals(value)) {
            out << ' selected="selected" '
        }
        out << '>' << noSelectionValue.encodeAsHTML() << '</option>'
    }

    private String optionValueToString(def el, def optionValue) {
        if (optionValue instanceof Closure) {
            return optionValue(el).toString().encodeAsHTML()
        }

        el[optionValue].toString().encodeAsHTML()
    }
}

This is my very first attempt and so I’m sure some not-so-best practices abound. Feedback and comments are welcome.

Update

  • Previously, the tag wouldn’t pass through extra attributes that were defined. This has been fixed.
  • My previous version didn’t take into account a bunch of stuff that Grails’ actual SELECT tag does. I was able to find the Grails implementation here. I modified it to take into account OPTGROUPs.

Formatting Tools

I added some formatting tools to the Comment Submission form. I need to change some of the images to better describe what they do. They should be mostly self-explanatory though. If you hover your mouse over the button, a little tooltip should appear, telling you what it does. If you click a button without selecting any text, you will simply see the appropriate tags (with sample text) inserted at the current location. If you select some text, and then click a button, the appropriate style is applied to the selected text.

The [dialogue]…[/dialogue] tags deserve special mention. I initially implemented them over a year ago. You can take a look at that journal entry to read the details, but I’ll mention them here again eitherway. The tags are… how do I put it… sensitive. You start off a dialogue section with a [dialogue] tag. Then on each line, you enter a statement in the form of Speaker: Dialogue. There can only be one speaker per line. When you are done, you close the section with [/dialogue].

So for example:

&#091;dialogue&#093;
l33t hax0r: i am teh leet hax0r!!11!!ONE11!
n00b: oh noes!11!!
l33t hax0r: i w1ll hax0r j00!11ELEVEN11!
n00b: i am haxed!!1 i r suprise!!11
l33t hax0r: pwnz0red!11
&#091;/dialogue&#093;

Will show up as:

l33t hax0r: i am teh leet hax0r!!11!!ONE11!
n00b: oh noes!11!!
l33t hax0r: i w1ll hax0r j00!11ELEVEN11!
n00b: i am haxed!!1 i r suprise!!11
l33t hax0r: pwnz0red!11

I hope that makes it clear. You can also click on the dialogue button to see a template for the dialogue section.

Oh yeah, the [code]…[/code] tags are different from the [codesnippet]…[/codesnippet] tags. The former is used for a line of code (or a small fragment) whereas the latter is used for a snippet of code (usually more than one line). You can see examples of their use earlier on in this post.

Dialogue Tag

I’ve made a new tag that makes it easier for me to make “dialogue tables” (like the one a couple of entries down). I love it! It’s pretty sweet! Custom tags rule! Woohoo! Oh yeah, regular expressions rule too!

Here’s how to use them:

You start of with a [dialogue] tag.

Inside the tag, you put your dialogues in the form of Speaker: Dialogue. Each line is a new dialogue. Every dialogue has to be on a single line by itself. Don’t put newlines inside a dialogue, or that’ll screw it up.

Finally, you end if with a [/dialogue] tag.

So for example:

&#091;dialogue&#093;
Me: Hey, I think this dialogue tag thing ROCKS!
You: Yeah dude, it's totally SWEET!
&#091;/dialogue&#093;

would look like:

Me: Hey, I think this dialogue tag thing ROCKS!
You: Yeah dude, it’s totally SWEET!

Isn’t that awesome?! You can use it in your comments! I’ve got some other custom tags, but I’ll talk about them later…

All original content on these pages is fingerprinted and certified by Digiprove
%d bloggers like this: