X

Geertjan's Blog

  • September 30, 2007

FreeMarker: Baked into NetBeans IDE 6.0 (Part 2)

Geertjan Wielenga
Product Manager
In Part 1, I talked about FreeMarker support in NetBeans IDE 6. It is definitely very cool in the context of code generation. I'm pretty sure that it has a big impact on chapter 19 of Rich Client Programming: Plugging into the NetBeans Platform. There, a whole bunch of Wicket-related artifacts are generated at the end of the New Web Application wizard. At the time, in the absence of FreeMarker, we had to create a pretty clumsy artifice that replaces macros in template files with values set by the user in the wizard. Now, however, with FreeMarker, not only will we be using an external approach to this, i.e., not an internal hacked approach, but also many things are much easier than before, which I hope to illustrate in this blog entry.

Let's say we have this wizard:

Now, the info from that wizard could be the input for a wide variety of outputs. Here's the FreeMarker template that defines my output:

Notice especially the highlighted bit, because there we have a bit of iteration going on! That's really cool, because now I don't have to handle that in my code. FreeMarker does it for me. I simply need to send some kind of array called 'types' (or anything else, of course, so long as the object I send has a name that is the same as the name used in the FreeMarker template) and then FreeMarker will iterate through it for me. I like that. I like that a lot.

Now, how to assign the above template to the wizard? Here's how my template is defined in the layer, notice the two bits in bold. The first bit points to the location of the template, which is in the same folder, in this case, as the layer file. The second highlighted bit tells the IDE to treat the template as a FreeMarker template:

<folder name="Other">
<file name="letter.html" url="letter.template">
<attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.newfiletemplate.Bundle"/>
<attr name="instantiatingIterator" newvalue="org.netbeans.modules.newfiletemplate.letter.LetterWizardIterator"/>
<attr name="template" boolvalue="true"/>
<attr name="templateWizardURL" urlvalue="nbresloc:/org/netbeans/modules/newfiletemplate/letter/letter.html"/><attr name="javax.script.ScriptEngine" stringvalue="freemarker"/>
</file>
</folder>

A second thing to discuss is how the values from the wizard end up being used in the FreeMarker template. If you use the Wizard wizard to generate the basic artifacts required by every NetBeans wizard, one of your new classes will be an iterator. In the iterator's instantiate() method, the generation process takes place, by means of the createFromTemplate(dataFolder, targetName) method. However, now you can include a map containing the objects that you want to pass. And where are they passed? To the FreeMarker template of course. Here's the instantiate() method for the above template:

public Set instantiate() throws IOException {
FileObject dir = Templates.getTargetFolder(wizard);
String targetName = Templates.getTargetName(wizard);
DataFolder df = DataFolder.findFolder(dir);
Object[] data = (Object[]) wizard.getProperty(LetterVisualPanel1.PRODUCTS.toString());
hashMap = new HashMap();
hashMap.put("url", wizard.getProperty(LetterVisualPanel1.URL));
hashMap.put("name", wizard.getProperty(LetterVisualPanel1.NAME));
hashMap.put("types", data);
FileObject template = Templates.getTemplate(wizard);
DataObject dTemplate = DataObject.find(template);DataObject dobj = dTemplate.createFromTemplate(df, targetName, hashMap);
FileObject createdFile = dobj.getPrimaryFile();
return Collections.singleton(createdFile);
}

Note the line in bold above. There my hashMap is included. And the map consists of a "url", a "name", and a "types", which are used in the FreeMarker template. Now, when the user completes the wizard, for the values shown above, they will see this:

There's quite a bit more possible with FreeMarker, and obviouly we need syntax coloring and so on, that I'm still investigating, such as something to do with the product license that I need to figure out. But I can already see the benefits of FreeMarker support in NetBeans IDE 6.0. And it's all really pretty cool and clever.

Join the discussion

Comments ( 3 )
  • Rohan Ranade Sunday, September 30, 2007

    Wicked cool! Thanks for sharing this.


  • Geertjan Wielenga Wednesday, June 30, 2010

    If you want a feature in NetBeans IDE, the answer is very very very simple: go to Issuezilla and create an enhancement request (and, also, blog about your request so that others know about your need and you can mobilize others to support you).


  • Dave Wednesday, June 20, 2012

    Hi,

    Great article. One question - how about using Freemarker when you use a 'Project Template' rather than single file.

    My instantiate method looks like this (unmodified from what netbeans generated). The unzipFile method goes through each file in the zipp'd bundle. Any idea how I can apply the freemarker script engine to this ?

    public Set/*<FileObject>*/ instantiate(/*ProgressHandle handle*/) throws IOException {

    Set<FileObject> resultSet = new LinkedHashSet<FileObject>();

    File dirF = FileUtil.normalizeFile((File) wiz.getProperty("projdir"));

    dirF.mkdirs();

    FileObject template = Templates.getTemplate(wiz);

    FileObject dir = FileUtil.toFileObject(dirF);

    unZipFile(template.getInputStream(), dir);

    // Always open top dir as a project:

    resultSet.add(dir);

    // Look for nested projects to open as well:

    Enumeration<? extends FileObject> e = dir.getFolders(true);

    while (e.hasMoreElements()) {

    FileObject subfolder = e.nextElement();

    if (ProjectManager.getDefault().isProject(subfolder)) {

    resultSet.add(subfolder);

    }

    }

    File parent = dirF.getParentFile();

    if (parent != null && parent.exists()) {

    ProjectChooser.setProjectsFolder(parent);

    }

    return resultSet;

    }


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha