Make your beans easy to customize using the BeanInfo interface and the Introspector class We want our beans to be easy to use, not just for users, but also (and especially) for application developers customizing our beans by changing their properties.In the September issue of JavaWorld, in my article “Double Shot, Half Decaf, Skinny Latte: Customize your Java,” we looked at how to make JavaBeans customizable. A simple naming scheme provides a minimal mechanism for giving an Integrated Development Environment (IDE) or other bean container object — like the BeanBox, for example — a way to let a user customize a bean. (For more information on the BeanBox, see my JavaWorld article “The BeanBox: Sun’s JavaBeans test container.”) When a bean needs to be customized, the IDE analyzes the bean’s class and creates a quick-and-dirty dialog box containing little edit widgets (called property editors) for each property, which it then pops up on the screen. Controlling, replacing, and extending what gets popped up is what this article is all about.This month, we’ll go over the conceptual groundwork for understanding the different customization facilities in JavaBeans. You’ll learn about the various customization levels and the classes used to implement them. Then, we’ll look at an example of one of the simpler forms of customization by using the BeanInfo interface, which tells the container more about the bean than it could figure out on its own. We’ll also write a very simple custom property editor (which lets you edit properties any way you like) and install it in the BeanBox. Along the way, we’ll touch on the Introspector class — the JavaBeans class used by IDEs and other bean containers to analyze class files. Next month, we’ll concentrate on code, writing and using a Customizer, registering property editors, and having a look at controlling bean visibility.Note: In this article, I’ll use the term IDE to mean “any container that cares about what’s inside a bean.” This is not, of course, limited to IDEs: Beans-aware browsers, bean testers like the BeanBox, information “buses,” and the like all may need to know what’s inside a bean. I use “IDE” because it’s only three letters, and because it’s one of the places where customization functionality is most commonly used.Levels of customizationAs beans become more complex, the number of properties (and, therefore, the number of getter and setter methods) begins to explode. More and more properties appear on the dialogs, and soon the dialogs we use to customize beans are oversized, awkward to use, and confusing.It may not be immediately clear to the user of the bean what all the different properties mean or how they relate to one another. Some properties, particularly indexed properties, may have no reasonable default screen representation. Others may simply be more “friendly” if edited graphically.The JavaBeans Specification and the java.beans package come to the rescue again, with a flexible, open, and extensible set of conventions, classes, and interfaces. These give you, the bean developer, control over what appears on the screen when someone adds your bean to their application and then wants to customize it. There are several levels of customization control, ranging from conforming to design patterns and letting the IDE create its own dialog, to writing a customization class that provides a complete GUI for configuring the bean.Customization level 1: Using design patterns If you’ve read the first customization article in this JavaBeans series (see the Resources section below if you haven’t), you already know that accessor (or getter and setter) methods change the internal state of a bean. You’ll also recall that design pattern is JavaSoft’s term for following the JavaBeans Specification’s naming and signature conventions for accessor methods, so that their containers can figure out the properties of the bean and how to set them. If you haven’t read the previous article yet, here’s a quick summary: A property is some subset of the internal state of a software object — in this case, a bean. A property’s accessor (or setter and getter) methods provide the only way to change a particular property from outside a bean. The JavaBeans Spec specifies that, for each property (let’s say it’s called Property and is of type SomeType), there should be getter and setter methods that look like this:SomeType getProperty() (getter)void setProperty(SomeType value) (setter)If a JavaBean conforms to the JavaBeans Spec, an IDE can analyze the bean, find methods whose names conform to the above pattern, and figure out what the properties are and how to set them. It can even whip up a customization dialog box on the fly, since it knows the property names, their types, and how to set and get them. If you’re not sure you understand this, see the September “JavaBeans” article that I referred to above.So, you’ve already learned the most primitive level of customization: design patterns. This is the “standard” way that IDEs and other bean containers figure out how to present a bean to an application developer for customization. There are some problems with relying solely on design patterns as a customization interface. First, since an IDE knows only the names and properties of an object but not what these properties mean, most will choose simply to list the properties in alphabetical order (if you’re lucky) in a dialog box or in a grid of some sort. Using design patterns, your customization dialog boxes can get pretty ugly.The dialogs also get huge. Think back to the BarChartBean example that we’ve developed over the past few articles. Here’s a picture of it to refresh your memory:This is a pretty simple bean, but look at that list of properties! I could think of a dozen more cool properties to add without even breaking a sweat: rotation, 3-D look, color changes at certain percentages, and so on. Now, imagine trying to configure a spreadsheet or word processor bean with this method: There could be literally hundreds of properties, all arranged in one long column. There also may be properties you simply don’t want to expose. For example, look at the customization dialog to the right of the BarChartBean in the image above. There are a few properties like font, foreground, and background, that don’t seem to relate to anything the BarChartBean does. (The BarChartBean doesn’t use text anywhere, and always completely fills its client area.) If you recall, these properties were inherited from java.awt.Component, so they show up on our customization dialog because they’re properties.The BeanBox is doing its level best to show you everything it knows about the BarChartBean; unfortunately, it’s showing you too much. I’m not even using all of the properties the BarChartBean presents, and worse, they’re confusing. What’s the difference between background and fillColor? (I know, because I wrote the bean, but that doesn’t mean my users know.) It would be best to hide properties that are of no interest to the user or that would actually cause problems for someone if fiddled with.Customization level 2: Providing a BeanInfo object The second level of customization, the interface java.beans.BeanInfo, addresses all of these problems. The BeanInfo interface lets your bean describe itself to anyone who asks (we’ll explain what “asks” means in the section on the class Introspector). In addition to describing properties, the BeanInfo interface can provide the IDE with a display name, an icon, lists of properties, methods (including constructors), event sets, and other information. (We’ll look at BeanInfo in greater depth in the section “‘Sup? The BeanInfo Interface” below.)There are some properties that would simply be better if displayed in a way that’s not setting an integer, color, or text field. One common example is an enumerated value: Maybe I’d like to choose between several different color schemes (“Spring Morning”, “Industrial Waste,” and so forth) from a drop-down list. Another might be setting the tabs in a Tabbean (which might be a sub-component of a larger PageLayout bean in a word processor, for example). Design patterns and BeanInfo simply don’t handle these cases directly, but the interface java.beans.PropertyEditor provides you, as a programmer, with a way to present the configuration of the property as a GUI object; in other words, you can write a property editor.Customization level 3: Providing custom property editors The third level of customization, java.beans.PropertyEditor and java.beans.PropertyEditorSupport, provides a way for you to write your own property editor. A property editor is a component that edits a single property of a certain type. When you click on a Color property to configure the color, the standard property sheet in the BeanBox pops up the standard property editor for properties of the type Color:The dialog box you see here is a standard property editor that the BeanBox uses to edit properties of type Color. The PropertyEditor interface (and PropertyEditorSupport convenience class) lets you specify a property editor of your own devising if the one the IDE provides doesn’t suit your needs.The PropertyEditor interface also helps you to implement enumerated types by its support of “tags,” the text values in the enumerated type. Some properties could be considered “logical” properties, composed of a composite of several actual properties in a software component. A good example of a logical property might be color balance, which simultaneously manipulates several variables at once at the implementation layer. Now, you could have a standard property sheet with properties like “Red Attenuation” or “Gamma,” but that needlessly exposes the implementation to the user. A much better way to display color balance might be to display the image with its current color balance and surround it with little thumbnails that “move” the image a few percent each direction in a color space. The user could then select the image that looks “best,” and the dialog could center on that image and redisplay all of the thumbnails. (Adobe PhotoShop has a dialog very much like this.)Customization level 4: Providing a Customizer The fourth and final layer of customization control is the interface java.beans.Customizer, with which you completely replace the IDE’s standard property sheet for the bean. You write a custom companion class that is delivered with the bean, and that class is used to configure the bean in place of the standard property sheet. If designed properly, customization dialogs for beans could be used as subcomponents in user interfaces in a target application. Imagine for a moment you had a PrintBean component that handled scheduling print jobs. Which interface would you rather use? Property SheetCustomizer How bean containers use customizationAlthough I’ve presented these types of customization control in order of increasing power (and increasing work to implement), an IDE actually looks for these controls in order of descending complexity. When it’s time to customize a bean, the IDE asks the bean for its customization information (BeanInfo). The IDE then gets the customizer class from the BeanInfo object. The customizer class will be either a class specified by the user (next month, we’ll learn how to write a customizer) or a property sheet created on-demand by the IDE. If the IDE creates its own customization dialog box (the property sheet), it’s filled with property editors, one per property. The list of properties comes from — you guessed it — the BeanInfo object, or, failing that, from introspection (literally, “looking into”) of the bean to find out its properties. Any custom property editors associated with the properties are included in the property sheet.Before we begin to discuss how to use the BeanInfo class, we need to go over one more topic: the Introspector. Dissecting a bean: the Introspector classI’ve been saying that IDEs and other beans-aware containers “ask” a bean for a BeanInfo object. What exactly does “ask” mean?The class java.beans.Introspector is designed specially for use by programs that need to find out about what beans’ capabilities are. IDEs, test containers like the BeanBox, beans-aware browsers, and so on need some standard way of finding out how to customize a bean. The Introspector class provides that interface.As a beans developer, although you probably won’t ever actually use the Introspector class yourself (unless you’ve got nothing better to do late on a Friday night), it’s important to understand what it does, since the Introspector is your “audience” when writing BeanInfo objects.The Introspector searches through the class file for your bean, looking for clues as to what the bean can do, how to customize it, and so forth. These clues are the “design patterns” we’ve discussed before. The Introspector class follows the naming rules of the JavaBeans Spec, assuming that whoever wrote the class followed them as well. So, for example, when the Introspector finds a method called int getElvis(), and another called void setElvis(int iElvis), it assumes that the bean has an int property called “Elvis” and adds that information to the list of properties it found. The Introspector method that is of the greatest interest to you looks like:public static BeanInfo getBeanInfo(Class beanClass) throws IntrospectionException This method is called by the IDE in order to find out how to customize BeanClass. (BeanClass is an object of type Class, which is the class object for your bean class, loaded into the JVM.) The Introspector is going to return a BeanInfo object whether you supply one or not. If you don’t supply a BeanInfo object, then Introspector will create one for you using the Java reflection mechanism to analyze the class file. It looks for property accessors, event sets, and methods that correspond to the design patterns laid out in the JavaBeans Spec. Just so you see what the code’s like, here’s (more or less) what the IDE would do:Class theBeanClass = Class.forName(className); Introspector clouseau = new Introspector; BeanInfo BeanInfo = clouseau.getBeanInfo(theBeanClass); Assuming no exceptions were thrown, the variable BeanInfo would contain all available information about the bean; we now move on to look at exactly what BeanInfo does know.‘Sup? The BeanInfo InterfaceThe BeanInfo interface is the crux of the whole customization scheme. When an IDE (or other container) asks an Introspector object for a bean’s BeanInfo, the Introspector goes looking for a class called BeanNameBeanInfo — that is, the name of the bean with the string “BeanInfo” appended. It looks first in the bean’s package, and if it doesn’t find it there, it searches the CLASSPATH for a such a class. (It’s a bit more detailed than that; if you want the full scoop, read the JavaBeans Spec.) If the Introspector finds a BeanInfo class, it creates an instance of that class and returns it; otherwise, it uses reflection on the bean class and builds a BeanInfo object from scratch, on the fly.The BeanInfo class supplies the container with information about the bean. Among other options, a BeanInfo object can provide the IDE with:Display name — a name to display for the bean classA short description of the classAn icon imageA list of properties (and their accessor methods)A list of methods (and their parameters), including constructorsA list of event sets — that is, the events that this object can produce and/or listen forProperty, method, and event set information is returned in the form of arrays of objects called “descriptors” (presumably, “describers” isn’t a word) — all subclasses of java.beans.FeatureDescriptor. A descriptor object is simply an object that describes a feature of a bean. The methods of BeanInfo that return this information are:getBeanDescriptor(): returns a single BeanDescriptor, containing miscellaneous information about the bean (like its icons, display name, and so on)getPropertyDescriptors(): returns an array of property descriptorsgetMethodDescriptors(): returns an array of method descriptorsgetEventSetDescriptors(): returns an array of event set descriptorsIn order to avoid having to implement all of these methods, there’s a convenience class called SimpleBeanInfo whose methods all return null. As useless as that may sound, it’s actually a handy class to have around. When the Introspector gets null from any of these methods, it goes off and uses reflection to figure things out for itself. This makes SimpleBeanInfo a useful base class for your new BeanInfo class: If you’d like to provide information other than the default — a reduced list of property descriptors, for example — you simply override the appropriate method of SimpleBeanInfo and allow the others to return null (providing default behavior). It’s much like using the “event adaptor” classes we discussed last month.Example: Return of the BarChartBeanAbove, we saw the property sheet (that is, the default customizer) for a BarChartBean in the BeanBox, which included annoying extraneous properties that could be confusing to users. Let’s create a BeanInfo class that will limit the properties visible to the bean user.As mentioned before, the BeanInfo class for the BarChartBean is called BarChartBeanBeanInfo, in conformance with the JavaBeans Spec. In addition to limiting ourselves to just a few properties (percent, fillColor, and floodColor, let’s say), let’s also add a little icon:and let’s give the bean a display label of Bar Chart. The following code demonstrates how to use the BeanInfo class to limit the properties that appear on the property sheet, and how to supply an icon for the bean. import java.beans.*; public class BarChartBeanBeanInfo extends SimpleBeanInfo { private final static Class myClass = BarChartBean.class; // // Note that here we create an array of 3 PropertyDescriptor // objects. The constructor takes just the name of the property, // and the bean class that implements it. // public PropertyDescriptor[] getPropertyDescriptors() { try { PropertyDescriptor flc = new PropertyDescriptor("floodColor", myClass); PropertyDescriptor fic = new PropertyDescriptor("fillColor", myClass); PropertyDescriptor pct = new PropertyDescriptor("percent", myClass); PropertyDescriptor[] list = { flc, fic, pct }; return list; } catch (IntrospectionException iexErr) { throw new Error(iexErr.toString()); } } // // This function returns a BeanDescriptor object, which specifies // general miscellaneous information about the bean. // public BeanDescriptor getBeanDescriptor() { BeanDescriptor desc = new BeanDescriptor(myClass); desc.setShortDescription("A configurable bar chart"); desc.setDisplayName("Bar Chart"); return desc; } // // This function returns a small icon which, in the BeanBox, // will appear in the Toolbox window next to the display name. // public java.awt.Image getIcon(int iType) { if (iType == BeanInfo.ICON_COLOR_16x16) { System.out.println("Getting image..."); return loadImage("BarChartIcon.gif"); } return null; } }; We include the .class file for this class in the JAR file, as well as the file “BarChartIcon.gif” — thereby indirectly placing these files in the CLASSPATH where the class loader can find them. When we run the BeanBox, here are the results:New property sheet, icon, and display nameNow, only those properties returned in the PropertyDescriptor list appear in the customizer. Also, on the left, you can see that the bean is called BarChart, and it has a little icon installed next to it.Now that you understand how to hide or make properties visible, let’s turn to the problem of changing how they are edited.Property editorsThe property sheet that the BeanBox provides you has two parallel columns: one with labels (the property names), and one with widgets used to edit the properties. These property-editing widgets are the property editors we’ve discussed before, and they are components in their own right. (Next month, we’ll learn how to reuse and/or replace them in the builder tool’s environment.) You can replace the default property editor for a particular property in one of several ways, assuming that you’re already providing a BeanInfo object.We don’t have a String property in the BeanBox, so let’s add ColorScheme, a property that sets both the flood and fill colors of the BarChartBean, when given a valid “color scheme name.” The code is straightforward: String colorScheme_; public BarChartBean() { // Must be valid on construction setColorScheme("Default"); //... continue with previous constructor code... } //... public String getColorScheme() { return colorScheme_; } public void setColorScheme(String scheme) { if (scheme == "Atrocious") { setFloodColor(new Color(10, 75, 10)); setFillColor(new Color(255, 128, 128)); colorScheme_ = scheme; } else if (scheme == "Hideous") { setFloodColor(new Color(255, 255, 0)); setFillColor(new Color(255, 95, 255)); colorScheme_ = scheme; } else if (scheme == "Revolting") { setFloodColor(new Color(255,12,128)); setFillColor(new Color(120, 240, 45)); colorScheme_ = scheme; } else if (scheme == "Drab") { setFloodColor(new Color(120,120,12)); setFillColor(new Color(200, 200, 0)); colorScheme_ = scheme; } else { // Else use default scheme = "Default"; } if (scheme == "Default") { setFillColor(Color.blue); setFloodColor(Color.red); colorScheme_ = scheme; } } The convenience class java.beans.PropertyEditorSupport provides an interface for creating property editors for simple enumerated types (where the user chooses an item from a list of strings). java.beans.PropertyEditorSupport is another one of those utility classes that lets you overload only those members you want. In this case, we only want to overload one function, String[] getTags(), which returns a list of “tags” corresponding to the string values of the enumerated type. This is a custom property editor, so we need to create a property editor class (we’ll see what to do with it in a moment):import java.beans.*; public class BarChartColorSchemeEditor extends java.beans.PropertyEditorSupport { public String[] getTags() { String ColorSchemes[] = { "Default", "Atrocious", "Hideous", "Revolting", "Drab" }; return ColorSchemes; } }; The interface java.beans.PropertyEditor says that, if getTags() returns a list of strings, a drop-down list property editor will be added to the property sheet to edit the property. (In this case, it’s a String property.) That’s all that’s necessary to create this property editor.Next, we need a way to associate this new property editor with the new property we’re going to add to the BeanInfo object. The java.beans.PropertyDescriptor has a method called setPropertyEditorClass(Class) that associates a property editor class with a property; therefore, when we add the new property to the list of PropertyDescriptors in BarChartBeanBeanInfo.getPropertyDescriptors(), we also associate the new property with our new class, like so:public class BarChartBeanBeanInfo extends SimpleBeanInfo { private final static Class myClass = BarChartBean.class; public PropertyDescriptor[] getPropertyDescriptors() { try { PropertyDescriptor flc = new PropertyDescriptor("floodColor", myClass); PropertyDescriptor fic = new PropertyDescriptor("fillColor", myClass); PropertyDescriptor pct = new PropertyDescriptor("percent", myClass); PropertyDescriptor psc = new PropertyDescriptor("colorScheme", myClass); psc.setPropertyEditorClass(BarChartColorSchemeEditor.class); PropertyDescriptor[] list = { flc, fic, pct, psc }; return list; } catch (IntrospectionException iexErr) { throw new Error(iexErr.toString()); } } //.. and so on... } This is how the receiver of the BeanInfo object knows to use the new BarChartColorSchemeEditor class to edit the ColorScheme property of the BarChartBean.When the BeanBox loads the BarChartBean, it finds the BarChartBeanBeanInfo object and asks it for a list of property descriptors. For each descriptor, it adds a property editor to the property sheet, and in the case of the ColorScheme property, it finds a property editor class (BarChartColorSchemeEditor) and uses that instead of the default. Note that when the BarChartBean is constructed, it must have a valid value for this property or the introspection will fail and the property will not appear.The result is a new property on the property sheet that uses a drop list to select one of several (equally aesthetically-distressing) color schemes:ConclusionThis month, we got an overview of the different types of customization that JavaBeans provide, and we got a start on using BeanInfo, the class that beans use to describe themselves to the containers that need to know about them. Next month we’ll concentrate on code, writing and registering our own property editors for individual properties, and writing a complete customizer which takes the place of the default property sheet. We’ll also go into some more depth on the functionality of the BeanInfo class.Mark Johnson has a BS in Computer and Electrical Engineering from Purdue University (1986). He is a fanatical devotee of the Design Pattern approach in object-oriented architecture, of software components in theory, and of JavaBeans in practice. Over the past several years, he worked for Kodak, Booz-Allen and Hamilton, and EDS in Mexico City, developing Oracle and Informix database applications for the Mexican Federal Electoral Institute and for Mexican Customs. He currently works as a designer and developer for Object Products in Fort Collins, CO. Java