GWT Generator Experiments – 3

September 14th, 2008 Alex Moffat Posted in GWT | No Comments »

What I want is a generator that makes handling CSS easier. Standard GWT has stylesheet elements that you can add to your module xml file. At runtime the generated JavaScript creates link elements to include the stylesheets.

An interesting alternative is provided by ImmutableResourceBundle and StyleInjector. This embeds the CSS into the generated JavaScript and uses a style element to add it to the document at runtime.

The stylesheet approach is nice because with an appropriate project structure you are able to edit the CSS files and see the changes in a browser without redeploying the application. It’s basically equivalent to adding the links to the html page yourself. For reusable modules it also provides a way to supply a default CSS file without requiring that the user of the module make any changes to their HTML pages.

However, this solution has a couple of downsides, just as adding the link element manually to the HTML page does. First, having more link elements in your document means more requests to the server to download all the CSS. Second, CSS files are cached by browsers, and while this is a good thing because it reduces the number of server requests it’s possible that changes to an existing file won’t be picked up by users when you want them to be.

The alternative of ImmutableResourceBundle and StyleInjector are nice because they reduce the number of requests made to the server and provide strong naming of the generated JavaScript file, so guaranteeing that any changes made to the CSS will be picked up at once. On the other hand, this solution makes development more difficult as you have to recompile whenever you change the CSS.

It would be nice to have a combination of the two techniques, that is a development mode that uses link elements with external style sheets, and a deployment mode that uses style elements and embedded CSS. In addition I’d like support for browser specific extension styles sheets. For instance if you have style.css, style_ie6.cc and style_safari.css then the appropriate browser specific file is used in addition to the base file.

Example

Here’s an example of how the system would be used. First create an interface extending CssResources. This defines methods to add style to a document. Each method is annotated with the names of the CSS files that it will add.


public interface CssExample extends CssResources {
    @Source({"/FileOne.css", "/FileTwo.css"})
    public void addStyle();
}

Then use GWT.create(...) to create an implementation of the interface and call addStyle() to apply the CSS to the document.


    private CssExample example = GWT.create(CssExample.class);

    public void onModuleLoad() {
        example.addStyle();
        ....
    }

The definition for the CssResources interface is fairly simple.


public interface CssResources {

    // @Source can be applied to methods only
    @Target(ElementType.METHOD)
    // Runtime retention needed to be able to use @Documented annotation
    @Retention(RetentionPolicy.RUNTIME)
    // @Documented annotation needed so that @Source annotations are reported in JavaDoc
    @Documented
    public @interface Source {
        /**
         * The names of the CSS resources
         */
        String[] value();
    }
}

I had to decide where the generator was going to look for the CSS resources. Originally I had support for searching in the classpath and the file system (by using an addition attribute on the annotation). Now I’ve removed the file system support. It’s easy enough to add directories to the classpath and this the only way to get files served in hosted mode anyway.

Generated Code

The generated code should be as small as possible because you don’t get the benefit of an IDE when you’re writing it. I moved all the common code to a utility class. So, for links to external stylesheets the code should look like


public class CssExampleImpl implements com.lombardi.gwtexample.generatoruser.client.CssExample {
    public void setStyle() {
      CssResourcesHelper.addLinkToStyleSheet("/FileOne.css");
      CssResourcesHelper.addLinkToStyleSheet("/FileTwo.css");
    }
}

whereas for embedding the code should be something like


public class CssExampleImpl implements com.lombardi.gwtexample.generatoruser.client.CssExample {
    public void setStyle() {
      CssResourcesHelper.injectStyleSheet(".labelOne {\n    color: red;\n}\n");
      CssResourcesHelper.injectStyleSheet(".textBox {\n    color: blue;\n}\n");
    }
}

I hope to be able to finish my generator experiments in the next post and provide source code for the complete CssResourceGenerator.

Leave a Reply