Platinum Partner
netbeans,netbeans platform

NetBeans Platform Idioms: Pluggable TopComponent (Part 3)

In my previous post of this series, I recalled with a quick example about how it is possible to instantiate objects by means of the layer.xml facility in the NetBeans Platform. The example code was at the bottom of the first page of the previous post:

<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
<filesystem>
<folder name="Roles">
<folder name="foobar">
<file name="Role1.instance">
<attr name="instanceClass" stringvalue="my.roles.Role1"/>
</file>
<file name="Role2.instance">
<attr name="instanceClass" stringvalue="my.roles.Role2"/>
</file>
</folder>
</folder>
</filesystem>
Incidentally, you should note that in the meantime I've updated my naming convention, using now the term “role” in place of “behaviour”.


What if you don't want to just instantiate a bean, but also need to set some properties? The Platform allows a variation of the layer.xml syntax as follows:

<file name="TitleActivator.instance">
<attr name="instanceCreate" methodvalue="it.tidalwave.netbeans.windows.role.TitleActivator.createInstance"/>
<attr name="pattern" stringvalue="[%s]"/>
</file>

Here you see that I'm using a static factory method that is available in the TitleActivator class; this method can initialize a “pattern” property. Unfortunately, this approach needs some cooperation from the instantiated class, as you need to code yourself the static factory method:

public class TitleActivator
{
protected String pattern = "%s";

@Nonnull
public static TitleActivator createInstance (@Nonnull final FileObject fileObject)
{
final TitleActivator instance = new TitleActivator();
instance.setPattern((String)fileObject.getAttribute("pattern"));
return instance;
}

...
}

Also, you need to write boilerplate code for each supported property and you're introducing in your instantiated class a dependency to the FileSystems API (by means of FileObject). You can't instantiate a plain Java Bean, such as a Swing component - unless you provide a specific static factory method in a utility class, including the initialization code for each property.

This is a annoying as requires too much boilerplate code, so I wrote a universal bean factory for layer.xml, that works with any regular Java Bean. For instance, you can write:

<file name="Bean.instance">
<attr name="instanceCreate" methodvalue="it.tidalwave.netbeans.role.util.BeanFactory.createInstance"/>
<attr name="class" stringvalue="com.acme.AcmeBean"/>
<attr name="property1" stringvalue="value1"/>
<attr name="property2" intvalue="2"/>
<attr name="property3" boolvalue="false"/>
</file>

Properties are accessed by introspection (for my convenience I've used BeanProperty from (Better)BeansBinding). The code of BeanFactory is listed below.

See you next time for the fourth part of this series.


package it.tidalwave.netbeans.role.util;

import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.jdesktop.beansbinding.BeanProperty;
import org.jdesktop.beansbinding.Property;
import org.openide.filesystems.FileObject;
import org.openide.util.Lookup;
import org.openide.util.Parameters;

public final class BeanFactory
{
private BeanFactory()
{
}

@Nonnull
public static Object createInstance (@Nonnull final FileObject fileObject)
throws ClassNotFoundException, InstantiationException, IllegalAccessException
{
Parameters.notNull("fileObject", fileObject);

final String className = (String)fileObject.getAttribute("class");

if (className == null)
{
throw new IllegalArgumentException("No required fileobject attribute: class");
}

final ClassLoader globalClassLoader = Lookup.getDefault().lookup(ClassLoader.class);
final Class<?> clazz = globalClassLoader.loadClass(className);
final Object instance = clazz.newInstance();

final List<String> propertyNames = Collections.list(fileObject.getAttributes());
propertyNames.removeAll(Arrays.asList("class", "instanceCreate"));

for (final String propertyName : propertyNames)
{
final Object propertyValue = fileObject.getAttribute(propertyName);
final Property property = BeanProperty.create(propertyName);
property.setValue(instance, propertyValue);
}

return instance;
}
}
{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}