When we are editing web pages in our favourite IDE we expect that it will provide code completion support for all tags and their attributes we use. It is true for plain HTML pages as well as for JSPs and of course XHTMLs. How does code completion works? Very simple: we need to include tag library in page template and that’s it! IDE should analyze this tag library and use tag metadata to provide code completion for page author automatically. Of course tag library should contain all neccessary information for tags, their attributes, functions, etc.
When we create tag library for JSP everything is straightforward: tag library schema support all this information. So we simply create something like that:
Listing 1.
-
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
-
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
-
version="2.0">
-
<description>description</description>
-
<display-name>My Tag Library</display-name>
-
<tlib-version>1.0</tlib-version>
-
<short-name>mytl</short-name>
-
<uri>http://my.site.com/tags/mytaglibrary</uri>
-
<tag>
-
<name>inputText</name>
-
<tag-class>com.mysite.my.taglib.InputTextTag</tag-class>
-
<body-content>empty</body-content>
-
<attribute>
-
<name>type</name>
-
<required>true</required>
-
<rtexprvalue>false</rtexprvalue>
-
</attribute>
-
…
-
</tag>
-
…
-
</taglib>
and in page template we include this tag library using taglib directive:
-
<%@ taglib prefix="mytl" uri="http://my.site.com/tags/mytaglibrary" %>
After that IDE does all the magic and when we try to insert new tag it helps us to correctly type it, reveals all its possible attributes, their state, etc.
Problem with facelet code completion.
What happens when we try to do the same thing for facelets? Problem is that facelet taglib DTD does NOT support attributes and their special settings. According to it, it supports only name of component/renderer, converter or validator. These names are then looked up in faces-config.xml and are mapped to appropriate java classes. Then facelet framework invokes getters/setters on these classes using reflection API.
Listing 2.
-
<facelet-taglib>
-
<namespace>http://ng.conject.com/jsf/facelets</namespace>
-
<tag>
-
<tag-name>input</tag-name>
-
<component>
-
<component-type>InputComponent</component-type>
-
<renderer-type>InputRenderer</renderer-type>
-
</component>
-
</tag>
-
….
-
</facelet-taglib>
What’s inconvenient in this approach is the lack of full support of code completion. IDE doesn’t know about tag attributes and their special settings, so it can’t help page author in fullfilling correct data. That leads to lot’s of typos, which can be found only during runtime.
Solution.
How to avoid it? I did it by creating another “dummy” tag library. It’s very similar to the one above with the exception that no tag class is specified. TLD is USED at runtime, it only helps to enable code completion and validation for tag attributes in IDE. Small excerpt from it:
-
<tlib-version>1.0</tlib-version>
-
<short-name>ngfl</short-name>
-
<uri>http://ng.conject.com/tags/ng-facelets</uri>
-
-
<tag>
-
<name>msg</name>
-
<!– No tag class needed – we are using it in facelets only –>
-
<tag-class/>
-
<body-content>empty</body-content>
-
<attribute>
-
<name>key</name>
-
<required>true</required>
-
</attribute>
-
</tag>
Now it’s time to do final thing: let’s make sure your IDE reads exactly this “dummy” tld and not the one in Listing 2. For that we need to map URI of facelet tag library to the location of dummy tag library. In IDEA it is done like that:
1) File->Settings->Resources
2) Add external resource (<facelet_taglib_uri -> <path_to_dummy_taglibrary>)
3) Enable “Project-related’ checkbox.

That’s it! Now when you edit your xhtml IDE provides you with full code completion support for all your tags and functions.



