Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Source code patterns used by the Visual Editor for Java in WebSphere Studio V5

Joe Winchester (winchest@uk.ibm.com), WebSphere tools developer, IBM Hursley Lab, United Kingdom, IBM
Joe Winchester is a member of the WebSphere Tools Development team working on the Visual Editor for Java for the Software Solutions group in Hursley, United Kingdom. You can reach Joe at winchest@uk.ibm.com).
Rich Kulp, WebSphere Studio development tools, IBM Research Triangle Park, North Carolina
Rich Kulp is an Advisory Software Engineer at IBM Raleigh Lab, North Carolina. He works on WebSphere development tools and is a member of the team working on the Visual Editor for Java. You can reach Rich at richkulp@us.ibm.com.
Gili Mendel, WebSphere Studio development tools, IBM
Dr. Gili Mendel is a Senior Advisory Software Engineer at IBM Raleigh Lab, North Carolina. He works on WebSphere development tools and is a member of the team working on the Visual Editor for Java. You can reach Gili at gmendel@us.ibm.com.
Peter Walker, WebSphere Studio development tools, IBM Research Triangle Park, North Carolina
Peter Walker is an Advisory Software Engineer at IBM Raleigh Lab, North Carolina. He works on WebSphere development tools and is a member of the team working on the Visual Editor for Java. You can reach Peter at walkerp@us.ibm.com.
Srimanth Gunturi, WebSphere Studio development tools, IBM Research Triangle Park, North Carolina
Srimanth Gunturi is a Staff Software Engineer at IBM Raleigh Lab, North Carolina. He works on WebSphere development tools and is a member of the team working on the Visual Editor for Java. You can reach Sri at sgunturi@us.ibm.com.
Jeffrey Myers (myersdj@us.ibm.com), WebSphere Tools Developer, IBM
Jeffrey Myers is a member of the WebSphere Tools Development team working on the Visual Editor for Java for the Software Solutions group in Raleigh, North Carolina. Jeff can be reached at myersdj@us.ibm.com.

Summary:  By understanding the rules by which the Visual Editor for Java interacts with Java source code, you can make the Visual Editor recognize a wide number of source styles and convert code from other builders.

Date:  25 Feb 2003
Level:  Intermediate

Activity:  3439 views
Comments:  

Introduction

IBM® WebSphere® Studio Application Developer (hereafter called Application Developer) lets you visually edit Java™ files that contain JavaBeans components. To use the editor, you click on the Java file you wish to edit and select Open With => Visual Editor from the pop-up menu. The Java file is the only resource that is persisted in the Application Developer workbench, so all of the information about the Java beans, their initial properties, and their relationships, is ascertained by parsing the source statements and recognizing coding patterns. After analyzing the source, the Visual Editor creates an internal model that it uses to build the Java beans shown on the visual canvas and in the Java Beans tree view. When you modify the source code while the Visual Editor is open, it analyzes the change and determines whether it affects the model. If so, then it updates the model to show the change, a process known as synchronization. You can also change the model directly using the Properties view, the Java Beans view, or the visual canvas, and this change is then propagated in the source code. This two-way synchronization between the model and the source code is known as round-tripping and is one of the key feature of the Visual Editor.

Because the Visual Editor for Java works directly with the statements in the Java source code, you can modify them and see the changes reflected in the Visual Editor. The advantage of this is that you are not restricted to making changes with the Visual Editor -- you can edit the Java source file in another editor, re-open the Visual Editor, and see the effect of your modifications. The Visual Editor doesn't at any point execute Java beans that you are editing, so instead it must try to reverse engineer from the source code the Java beans that will be constructed at run time and the properties that will be set. If the source code doesn't follow the patterns recognized by the Visual Editor, then the Java beans shown on the visual canvas will be incomplete or different from those that actually appear at run time. This article describes in detail the source code patterns used by the Visual Editor, as well as problems that can occur when the source code is outside the list of recognized styles.


Recognizing a Java bean

When the Java source is analyzed for patterns, the first step is to determine what Java beans are present. A Java bean must meet the following conditions:

  • If the Java bean is to be rendered on the graphical canvas free-form, it must be declared as an instance field (unless it is added to a parent that is declared as an instance field).
  • The field must start with ivj , unless the class is a descendent of java.awt.Component.
  • A method must assign the field an instance using a constructor or a static method call.

Some examples will help explain these three conditions.

Field declaration

To be a Java bean included in the Visual Editor's model, a field declaration must be present. In the class MyClass the fields ivjString, aString , and aFrame are possible Java beans:

public class MyClass{ 
  protected String ivjString; 
  public String aString; 
  private java.awt.Button button;
}

Field name begins with ivj for non-visual Java bean

Some classes contain many fields that the user may not wish to see in the Java beans model. This could include instances of URLs, FileDescriptors, or Strings that are part of the working state of the class but should not be included in the visual canvas. To restrict the number of unnecessary Java beans on the visual canvas and in the Java beans view, fields must start with ivj. An exception to this rule is made for visual Java beans, which are displayed on the canvas with an image of how they will appear at run time. (A visual Java bean is one whose class extends java.awt.Component, either directly or indirectly in its hierarchy.) The visibility of a field is not used to determine whether it is a candidate Java bean.

String ivjString

This is a candidate Java bean because the field name starts with ivj.

String aString

The field name does not being with ivj so this is not a candidate Java bean.

java.awt.Button button

Even though the field name does not start with ivj it is a candidate Java bean because java.awt.Button is a descendent of java.awt.Component in its class hierarchy.

Once a field has been determined as being a candidate Java bean that doesn't mean that it will appear in the Visual Editor's model. It must still satisfy the following rules to be included:

Method to instantiate the Java bean

For every Java bean that is included in the Visual Editor's model an object is created at design time. This object is used to determine the values shown by the Properties view for any unset properties, and if the Java bean is visual then a graphic of the live instance is shown on the canvas. After determining that a field is a candidate Java bean the Visual Editor code parser will look for a method that contains a statement responsible for setting the field to its initial value. A method is considered as being the one that sets the initial value if it has an assignment statement whose expression is a constructor or a static method call. The name or return type of the method, or its visibility, are not used in any way to determine whether it assigns the field or not. If there is more than one candidate method the first one that is defined is used. The following are examples of methods together with the reasons why every field is or isn't included in the model of Java beans.

private java.awt.Button button;
private java.awt.Button getButton(){
if ( button == null ) {
button = new
java.awt.Button()
}
}
The button is recognized as a Java bean because the field is assigned a value using the defaultConstructor of java.awt.Button. This style of method where the field is lazily instantiated in a get method that returns it is the one used by the Visual Editor for new Java beans that are added to the class being composed using the palette. Lazy instantiation is a technique used where a field is assigned a non-null value only if it is null, as occurs with the assignment being done within the if ( ivjButton == null ){..} block.
private java.awt.Button button;
private String ivjString;
public void createFields(){
ivjString = new String("Frog");
button = new java.awt.Button();
}
Both button and ivjString are included as Java beans in the Visual Editor model because the method contains a statement that assigns the field using a constructor. The method doesn't return either of the two Java beans it instantiates, nor does it instantiate the fields lazily, nor does it have a name that matches either of the fields.
private java.awt.Button button;
private String ivjString;
public void createFields(){
vjString = "Frog";
button = MyButtonManager.getDefaultInstance();
}
ivjString is not included as a Java bean because the assignment expression is to a literal constant of "Frog". The expression assigning the field a value must create the instance using either a constructor or a static method call. The field button is included as a Java bean because it is assigned a value by calling the static method: MyButtonManager.getDefaultInstance();.
private java.awt.Button button;
public void createFields() {
button = MyPanelManager.SINGLETON;
}
button is not recognized as a Java bean because the method that assigns it a value does so by using a static field SINGLETON on the class MyButtonlManager. Only static method calls are recognized as Java beans assignment expressions - not static field references.
private String button = new
java.awt.Button();
public void createFields(){
button.setLabel("OK");
}
button is not recognized as a Java bean. Although it is assigned during its field declaration, this is not sufficient because it must be assigned its initial value within a method. The createFields() method sets property values on the button field, but because it doesn't actually assign button with an instance, the button field is rejected as a candidate Java bean.

Once a field has been recognized as containing a Java bean, it is then included in the model for the Visual Editor. All entries in the model are shown in the Java beans tree view and also on the visual canvas. An actual Java bean instance is constructed by the Visual Editor using the expression contained in the assignment expression. If the Java bean is visual, then an image of its graphic is used on the canvas. Otherwise it is shown with an icon and its name. The button instance created will be shown at its preferred size because it has no explicitly set size or bounds property.



StringAndButtonAtDefaultSize.gif

Property values

When a Java bean is found in the source, the next step is to find its initial property values. These are things such as a button's initial label, a frame's size, or a panel's color. To determine the initial property values, the method that was found to be the one assigning the Java bean field with an instance, as described in the above sections, is analyzed for its set methods that have the Java bean field as the receiver. To be included as a candidate property value, the method name must match that on the set method of the java.beans.PropertyDescriptor. For the majority of cases, this means that the method should be named with the word set + property name and have a single argument that has the same type as the property. For example, the method public void setLabel(String aString) is the set method for the label property of the class java.awt.Button.

Simple property setting

In the simplest case, a method is recognized as being the one that initializes a Java bean because it assigns it a value, and the values of set methods associated with PropertyDescriptors are applied to the live instance. The method createFields() initializes button, so it is recognized as being its initializer method, and setLabel is the set method for the label property.

private java.awt.Button button;
private void createFields(){
button = new java.awt.Button();
button.setLabel("Frog");
}
ButtonFrogLabel.gif

If there is more than one set method for the same property in the initialize method, then the first one is used, as shown below where setLabel is called twice with values of "Dog" and "Frog".

private java.awt.Button button;
private java.awt.Button getButton(){
if ( button == null ) {
button = new java.awt.Button();
button.setLabel("Dog);
button.setLabel("Frog");
}
return button;
}
ButtonDogLabel.gif

Try-catch blocks

Set methods within try blocks are recognized as being initial property values, while those in catch blocks are not. In the code below, the button is red but doesn't have the label "Dog", because catch blocks are not expected to be executed within normal program execution and therefore are not responsible for setting a Java bean's default state.

private java.awt.Button button;
private java.awt.Button getButton(){
button = new java.awt.Button();
try {
button.setBackground(java.awt.Color.red);
} catch ( Exception exc ) {
button.setLabel("Dog);
}
return button;
}
RedButton.gif

Property values used in constructor arguments

The actual assignment expression is evaluated to construct the Java bean, so in the example below, where button is instantiated with the constructor new java.awt.Button("Dog"), it will appear in the visual canvas with the label "Dog". It also will appear with a red background because of the setBackground(java.awt.Color.red) method in the initializer method for button.

private java.awt.Button button;
private java.awt.Button getButton(){
button = new java.awt.Button("Dog");
button.setBackground(java.awt.Color.red);
return button;
}
RedDogButton.gif

However, because there is no setLabel method called for the button, although the label value is applied to the live instance, the Java bean property is not recognized as being set in the constructor and will not appear as a set value in the Properties view. Set properties are shown with a > against them in the Properties view, although unset properties are shown with the value returned by the Java bean's get method.



BackgroundSetLabelNot.gif

Therefore, if the label property is set in the Properties view to another value, such as "Cow", this will result in a new setLabel method being generated. The constructor argument will not be altered.

private java.awt.Button getButton() {
  button = new java.awt.Button("Dog"); 
  button.setLabel("Cow"); 
  //... 
}

When a property value set method is found, the assignment expression is then evaluated to construct an instance of the object that is used as the argument to the set method that is invoked on an instance of the receiver. Not all expressions can be successfully evaluated, as described below in the section Expressions that can be evaluated.

When a set method is found whose receiver is a Java bean, it is only evaluated by the Visual Editor if the set method is present on a property. The list of properties of a Java bean is determined by introspecting the class. Introspection is a process that tries to determine whether there is an explicit BeanInfo class for the Java bean, and failing that, a default one is created that matches up get and set method pairs by name. BeanInfo classes are typically created to supply information that is used by a tool such as the Visual Editor, such a property editor or customizer classes. A common pitfall of the Visual Editor is that a property has not been included in a BeanInfo class because the developer didn't want it to be shown to the user in the Properties view; however they still want the result of set methods in the code to be parsed and evaluated. In these circumstances, the BeanInfo should be changed to return the java.beans.PropertyDescriptor that defines the set method. However hide it with setHidden(false); so that it won't appear in list of available properties in the Properties view. Another common error is that BeanInfo classes do not return inherited properties, which therefore prevents all of their set methods from being evaluated. To correct this, the BeanInfo class should return the inherited properties in its bean descriptor with the following code:

public BeanInfo[] getAdditionalBeanInfo(){ 
    try { 
        return new BeanInfo[] { 
            Introspector.getBeanInfo(getBeanClass().getSuperclass()) 
        }; 
    } catch (IntrospectionException e) { 
        return new BeanInfo[0]; 
    } 
}


Properties on the class being edited

In addition to being able to set properties on Java beans that are defined in fields, the Visual Editor lets you set properties on the class you are editing. The class being edited is treated differently by the Visual Editor, because the receiver of the property sets methods is the this instance. Whether the this instance is included in the Visual Editor's model of Java beans is determined by whether the class has any properties. These properties would normally be shown in the Properties view, so they should have get and set methods and are obtained by performing introspection of the class. If the this instance is included in the Java beans model, it will appear in the Java beans tree view and visual canvas as for any other Java bean, except that it cannot be deleted or renamed because there is no field to remove or rename.

To determine the set methods that are responsible for creating the initial state of the this instance, the Visual Editor looks for a method called initialize(). If this method doesn't exist, it is created the first time a property is set on the this instance using the Visual Editor's viewers. If a default constructor does not exist when the initialize() method is created, then one will be added to the class that with a method call to the initialize() method. If the default constructor does exist when the initialize() method is created, it is only modified to call the initialize() method if the constructor has no other statements within it.

In the example code below, an instance for the class being edited is created because MyPanel has settable properties that it inherits from its superclasses. This instance is labelled this in the Visual Editor. A default constructor calls the initialize() method, although for parsing, the Visual Editor needs the constructor or method call to initialize() to be there. The initialize() method calls setSize(... and setBackground(..., and both of these are recognized as properties and applied to the this Java bean instance. The receiver of the methods can be explicit, such as this.setSize(50,50), or implicit, such as setBackground( java.awt.Color.cyan). Both are equally valid.

public class MyPanel extends Panel {
public MyPanel() {
super();
initialize();
} private void initialize() {
this.setSize(50, 50);
setBackground(java.awt.Color.cyan);
}
}
CyanThisPanel.gif

The exception to the initialize() method being used for property settings on the class being edited is if the class is an applet. Applets extend either java.applet.Applet or javax.swing.JApplet and are designed to run within a viewer launched by a Web browser. As part of their protocol, applets have a method public void init() that is called by the applet viewer to construct its initial state, and the Visual Editor parses the init() method, rather than initialize(), to look for initial property settings. This is shown below with MyApplet, with its size set to be 50,50 in the init() method and its background to be cyan in the initialize() method. The live Java bean, however, is shown at 50,50 but without a cyan background, because the initialize() method has no significance in Applet subclasses. When properties are set through the Properties view, the Visual Editor will detect whether the class being edited is an Applet and generate the code in the correct method.

public class MyApplet extends Applet {
public MyApplet() {
super();
}
public void init() {
this.setSize(50, 50);
}
private void initialize() {
this.setBackground(java.awt.Color.cyan);
}
}
WhiteApplet.gif

Relationships between Java beans

The above examples show set methods whose argument expression fully describes how to construct the object that will be used for the set method. For example, the argument of the method setBackground is the statement java.awt.Color.red. In addition to the statement for creating the argument object being fully described between the parenthesis of the set method, the argument object can be a reference to another Java bean -- a reference to the field name that describes the Java bean, or to the method that instantiates the Java bean if the method returns the Java bean. If the argument expression does reference a field, then this field must have followed the rules to be recognized as a Java bean by the visual editor -- namely it must have a method that instantiates it and it must begin with ivj (unless it is a visual Java bean).

Java beans defined as instance variables

In the example below, the field ivjColor is recognized as a Java bean because it begins with ivj (java.awt.Color does not descend from java.awt.Component and is not a visual Java bean, otherwise any field name could be used). The getButton() method contains an assignment expression that sets ivjColor using a constructor, so ivjColor is included as a Java bean in the Visual Editor model. The argument to setColor(ivjColor) is a reference to the field itself, so the Visual Editor will use the actual Java bean instance it creates for ivjColor as the argument object when it invokes the method.

private java.awt.Button button;
private java.awt.Color ivjColor;
private java.awt.Button getButton(){
if ( button == null ) {
button = new java.awt.Button();
ivjColor = new java.awt.Color(255,0,0);
button.setBackground(ivjColor);
}
return button;
}
RedButton.gif

Java beans defined as method variables

In the code below, even though the field ivjColor is scoped within the method that uses it and is not an instance field, it is still recognized as being a Java bean by the Visual Editor:

private java.awt.Button button;
private java.awt.Button getButton(){
if ( button == null ) {
button = new java.awt.Button();
java.awt.Color ivjColor = new
java.awtColor(255,0,0);
button.setBackground(ivjColor) } return button; }
RedButton.gif

Java bean instances assigned values from static fields

Java bean instances must be assigned with either a constructor or a static method call. Because ivjColor is assigned a value using the public method call java.awt.Color.red, it is not recognized as being a Java bean by the Visual Editor. The warning sign shown on the visual graphic for the button indicates an error applying a property-to-button instance. This is described more in the section Expressions that can be evaluated below. This is different from property values where static fields can be used (for example, button.setBackground(java.awt.Color.red) is valid), and the rule applies only to initialization of a field representing a Java bean.

private java.awt.Button button;
private java.awt.Button getButton(){
if ( button == null ) {
button = new java.awt.Button();
java.awt.Color ivjColor = java.awt.Color.red;
button.setBackground(ivjColor)
}
return button;
ButtonWarning.gif

Non-visual Java bean fields must start with ivj

Field names for non-visual Java bean instances must begin with ivj. In the code below, because the java.awt.Color instance field name is color with no ivj prefix, it is not recognized as a Java bean. The Visual Editor VM recognizes this and puts a yellow warning sign over the graphic for the Java bean to indicate that some of its properties were not applied correctly.

private java.awt.Button button;
private java.awt.Button getButton(){
if ( button == null ) {
button = new java.awt.Button();
java.awt.Color color = new
java.awt.Color(255,0,0);
button.setBackground(color)
}
return button;
}
ButtonWarning.gif

Property values assigned as the result of method calls

If a property value's set method argument is not directly to the field ivjColor but is instead to a method call such as getColor(), then the Visual Editor can process it as long it recognizes that getColor() is an initializer method for a Java bean. In the code below, the Visual Editor has recognized that ivjColor is a Java bean and that the getColor() method returns the field ivjColor. It therefore sets the button's background color to the value of the field ivjColor, which is assigned a value with the constructor new java.awt.Color(255,0,0).

private java.awt.Button button;
private java.awt.Color ivjColor;
private java.awt.Button getButton(){
if ( button == null ) {
button = new java.awt.Button();
button.setBackground(getColor());
}
}
private java.awt.Color getColor(){
if ( ivjColor == null ) {
ivjColor = new java.awt.Color(255,0,0);
}
return ivjColor;
}
RedButton.gif

Container and its components

The relationships shown up till now have been for property values, where a set method that is part of a property is used to establish the link between the two Java beans. In addition to set methods described in properties, other methods can establish relationships for different types of Java bean classes. An example of this is the relationship between a java.awt.Container, the root superclass of the visual classes such as java.awt.Frame and javax.swing.JPanel, and its child components. No single argument set method describes this property, so the Visual Editor looks for specific add(Component...) methods to determine whether a Java bean component is a child of the container or not.

In the code below, the method panel.add(getButton(),null) creates the relationship between the panel Java bean and the button, adding the button as a child component to the panel.

private java.awt.Panel panel;
private java.awt.Button button;
private java.awt.Panel getPanel() {
if ( panel == null) {
panel = new java.awt.Panel();
panel.add(getButton(), null);
}
return panel;
}
private java.awt.Button getButton() {
if ( button == null) {
button = new java.awt.Button();
button.setBackground(java.awt.Color.red);
}
return button;
}
PanelWithRedButton.gif

In addition to public void add(java.awt.Component child, Object constraint) the following methods on container will be recognized by the Visual Editor as creating a parent-child relationship between a container and a component.

public void add(java.awt.Component child); 
public void add(String name, java.awt.Component child); 
public void add(java.awt.Component child, Object constraint, int index);

Although all of the above methods are recognized as being a component added as child of a container, for the live Java beans instances, the indexed add method is always used by the Visual Editor's JVM (the last in the above list). All three of the methods, however, use the java.awt.Container method protected void addImp(Component aComponent, Object aConstraint, int anIndex), so if you have any specialized subclass behavior that you want to be used by the Visual Editor JVM, then you should specialize the protected method to ensure that your code will be executed. In addition to java.awt.Container and the relationship to its child component Java bean, there are other classes that have relationships that are recognized by the Visual Editor.

javax.swing.JTable

The relationship between a javax.swing.JTable and its child columns is recognized by the method public void addColumn(TableColumn). To help you see the columns, preview data with five rows is created on the visual canvas. A JTable can either show its explicit child columns, or else determine the number of columns from its model by calling the method public int getColumnCount(). Which of these two techniques is used by the JTable, both at run time and design time by the Visual Editor, is determined by the value of the boolean property autoCreateColumnsFromModel:

import javax.swing.JTable;
public class MyJTable extends JTable {
private javax.swing.table.TableColumn
ivjTableColumn = null;
private javax.swing.table.TableColumn
ivjTableColumn1 = null;
public MyJTable() {
super();
initialize();
}
private void initialize() {
this.addColumn(getIvjTableColumn());
this.addColumn(getIvjTableColumn1());
this.setAutoCreateColumnsFromModel(false);
}
private javax.swing.table.TableColumn
getIvjTableColumn() {
if(ivjTableColumn == null) {
ivjTableColumn = new
javax.swing.table.TableColumn();
}
return ivjTableColumn;
}
private javax.swing.table.TableColumn
getIvjTableColumn1() {
if(ivjTableColumn1 == null) {
ivjTableColumn1 = new
javax.swing.table.TableColumn();
}
return ivjTableColumn1;
}
}
JTableWithColumns.gif

javax.swing.JTabbedPane

A JTabbedPane has its children defined by the method public void addTab(String title, Icon icon, Component component, String tip). The component argument is the child tab component, although the other arguments are used to evaluate all of the parameters passed to the actual method called on the Visual Editor's Java bean. In addition to the four argument methods, the Visual Editor will parse and recognize the shorter methods public void addTab(String title Component component) and public void addTab(String title, Icon icon, Component component).

import javax.swing.*;
public class MyTabbedPane extends JTabbedPane {
private javax.swing.JButton jButton = null;
private javax.swing.JPanel jPanel = null;
public MyTabbedPane() {
super();
initialize();
}
private void initialize() {
this.addTab("A Button", new
ImageIcon(getClass().getResource("/button.gif")),
getJButton(), null);
this.addTab("A Panel", null, getJPanel(), null);
this.setSize(180, 110);
}
private javax.swing.JButton getJButton() {
if(jButton == null) {
jButton = new javax.swing.JButton();
jButton.setText("OK");
}
return jButton;
}
private javax.swing.JPanel getJPanel() {
if(jPanel == null) {
jPanel = new javax.swing.JPanel();
}
return jPanel;
}
}
JTabbedPane.gif

Expressions that can be evaluated

After parsing the source and creating a model of the Java beans and their initial property values and relationships, the Visual Editor for Java then creates actual prototype instances. It does this on a separate target VM from the one in which Application Developer is running. The prototype instances are used by the Properties view to show the value of unset properties, and if the Java bean is descended from java.awt.Component, then its graphic is shown on the visual canvas.

To create the prototype instances, at no point is the class that is being edited actually instantiated. Instead, each expression that instantiates a Java bean is evaluated and methods are executed on the target VM. Because there is no way to evaluate Java statements in a JRE without compiling them into bytecodes, the Visual Editor parses each expression to create a set of find-grained statements that are then evaluated on the target VM using the Java reflection API. For example, in the statement button.setBackground(java.awt.Color.red); the argument value java.awt.Color.red is parsed and determined to be a reference to the static field red on the class java.awt.Color. In the target VM, the class java.awt.Color is looked up and the field value retrieved using
Class.forName("java.awt.Color").getField("red").get(Class forName("java.awt.Color");. The resulting color is then used as the argument to the set method. For example:

button.getClass().getMethod("setBackground", new 
Class[] { java.awt.Color.class }).invoke(button,color);

Property value set method throws an exception

If a property set method throws an exception on the target VM, the Visual Editor will re-create the Java bean that is the receiver of the method, but will flag the errant property as being in error. The property that caused the exception will then not be applied to the prototype instance. Thus the live Java bean remains a working instance for longer so that the result of the remaining properties can be seen. To indicate that an error occurred when a set method was applied, a warning sign is shown on the visual canvas and in the Java beans view. If you select a Java bean with an error, the exception message, together with the name of the errant property, is displayed in the workbench status bar.

For example, the orientation property of javax.swint.JScrollBar can only be 0 (vertical) or 1 (horizontal). In the code below, when the value 9 is sent to the target VM, the prototype instance for JScrollBar throws an illegal argument exception. The Java bean is then re-created without the bad orientation property applied, although its other properties such as size are still set. A yellow warning triangle indicates that the Java bean encountered an error, so there is a mismatch between the data shown on the visual canvas and the data that would be shown at run time.

private javax.swing.JScrollBar jScrollBar;
private javax.swing.JScrollBar getJScrollBar() {
if( jScrollBar == null) {
jScrollBar = new javax.swing.JScrollBar();
jScrollBar.setSize(60, 71);
jScrollBar.setOrientation(9);
}
return jScrollBar;
}
JTableVisualError.gif

A warning sign is also shown in the Java Beans viewer, and when this is selected, the name of the errant property and the exception error message is shown in the workbench status bar.



JScrollBarStatusError.gif

Expression too complex to be evaluated

In addition to errors that occur on the target VM because of exceptions thrown by property set methods, some expressions are too complex for the Visual Editor to evaluate through reflection. Examples include methods that reference any program state other than a Java bean field directly. Expressions that reference non-static inner classes also can't be evaluated because they are too complex to be constructed through reflection, since non-static inner classes must be created only from within the outer class, which is the class being edited.

In the code, below the argument to the setModel(... method is an anonymous inner class. This cannot be instantiated by the target VM because it requires the creation of the outer class, which the one being edited may have not even compiled yet. Because the model cannot be applied, the preview data used by the Visual Editor of a 5x5 row column grid is shown, together with a yellow warning sign. The status bar shows that the expression is too complex for the Visual Editor to evaluate.

private javax.swing.JTable jTable = null;
private javax.swing.JTable getJTable() {
if ( jTable == null) {
jTable = new javax.swing.JTable();
jTable.setSize(138, 95);
jTable.setModel(new
DefaultTableModel(){
public int getColumnCount(){
return 2;
}
});
}
return jTable;
}
JTableWithError.gif

The error message shown in the status bar of the workbench indicates that the warning error is because the model property's set method argument is too complicated to be evaluated. The error symbol is shown against the JTable because the Visual Editor preview may look different at design time than at run time (when the set method will be applied, as the class has been compiled).



JTableTooComplexMessage.gif

In addition to inner classes, some types of expressions are outside the scope that the Visual Editor can parse and evaluate correctly. In the code below, the initialization expression button.setLabel(x); cannot be evaluated for two reasons. First, it references a field x that is a program state that cannot be evaluated by the target VM. If the field x were renamed to ivjX, it would be recognized as a non-visual Java bean, but in this case it is still too complex to evaluate because arithmetic expressions that are not actual method calls but are instead optimized by the Java compiler (such as +, -, /, or *) cannot be expressed using the Java reflection API.

private java.awt.Button button;
private java.awt.Button getButton() {
if ( button == null) {
button = new java.awt.Button();
button.setSize(75, 25);
String x = "O" + "K";
button.setLabel(x);
}
return button;
}
ButtonErrorBig.gif

Selecting the button will show the reason for the error in the status bar message:


LabelComplexError.gif

Use of non-null constructors to instantiate a Java bean

In the code below, because the constructor for MyJavaBean references this as its argument, this cannot be evaluated by the target VM using reflection. The only exceptions to this rule are java.awt.Dialog, java.awt.Window, and any subclasses of either of these when they are used within a Frame, and the prototype instance will be instantiated correctly.

private MyJavaBean ivjMyJavaBean;
private MyJavaBean getIvjMyJavaBean() {
if( ivjMyJavaBean == null) {
ivjMyJavaBean = new MyJavaBean(this);
}
return ivjMyJavaBean;
}
MyJavaBeanConstructorError.gif

Even if the Java bean is visual, no graphic for it is shown in the design view because it cannot created, and a red X symbol is shown instead. This also applies to all Java beans that throw exceptions during the construction, and selecting the Java bean in the Java beans viewer or on the design canvas will show the reason for the exception in the status bar.



MyJavaBeanThisStatusError.gif

Strings from resource bundle files

In the code below, the initialization string resbundle.getString("OK_label") references the static field resbundle, so it cannot be evaluated. Rather than flagging this as an error however, the Visual Editor recognizes that this is the syntax for retrieving a string value from a .properties file, and it calls the property set method with a String value of the bundle key with curly braces around it.

private static java.util.ResourceBundle resbundle
= java.util.ResourceBundle.getBundle("bundle");
private java.awt.Button button;
private java.awt.Button getButton() {
if ( button == null) {
button = new java.awt.Button();
button.setSize(75, 25);
button.setLabel(resbundle.getString("OK_label"));
}
return button;
}
OKButtonWithBundleKey.gif

Sample pattern conversions

This section illustrates a number of common issues related to code-parsing patterns. They are based on code samples created either by another builder or included in Swing documentation that requires modification before it can be used in the Visual Editor.

Swing JPanel and JButton

In the first scenario, the JPanel is defined as an instance field, but the jButton isn't. The JButton is defined locally in the method that instantiates the JPanel and added directly to it. The Visual Editor includes the JPanel because it is a field (see Recognizing a Java Bean), and it includes the button on the panel because it is an argument to the add(Component,Object) method even though it is a local method field and not a class field.

private javax.swing.JPanel getJPanel(){
if(jPanel == null){
jPanel = new javax.swing.JPanel();
JButton jButton = new javax.swing.JButton();
jButton.setText("Good, I am in");
jPanel.add(jButton, null);
jPanel.setSize(120,60);
}
}
panel004.jpg

In the next scenario, the field that adds the button to the jpanel is commented out. The panel remains on the graphical canvas, but the button does not. It is no longer a child of the jpanel, so it is not shown in the Swing JPanel instance, and because it is not defined as an instance field, it is not rendered as a top-level Java bean.

private javax.swing.JPanel getJPanel(){
if(jPanel == null){
jPanel = new javax.swing.JPanel();
JButton jButton = new javax.swing.JButton();
jButton.setText("Bad, I am out");
// jPanel.add(jButton, null);
jPanel.setSize(120,60); } }
panel005.jpg

In the last scenario, the declaration of the button field is moved from the method getJPanel() to be an instance field in its own right. Even though the button is still no longer a child of the jpanel because it satisfies the rules to be rendered on the graphical canvas (it is declared as an instance field and assigned a value in a method) it appears on the Visual Editor alongside the jpanel.

private JButton jButton = null;
private javax.swing.JPanel getJPanel(){
if(jPanel == null){
jPanel = new javax.swing.JPanel();
jButton = new javax.swing.JButton();
jButton.setText("Good, I am in again");
// jPanel.add(jButton, null);
jPanel.setSize(120,60);
}
}
panel006.jpg

MyPanel sample

This sample begins with the assumption that the code you wish to parse with the Visual Editor should appear as shown below: a JPanel with a JTextArea and a JButton using GridBagLayout.


image007.jpg

The code that is originally written to create this is as follows:

Before
import javax.swing.*;
import java.awt.*;
public class MyPanel extends JPanel{
private JButton jButton1 = new JButton();
private JTextArea jTextArea1 = new JTextArea();
private GridBagLayout gridBagLayout1 = new GridBagLayout();
public MyPanel(){
try{
jbInit();
}
catch(Exception e){
e.printStackTrace();
}
}
private void jbInit() throws Exception(){
ivjButton1.setText("MyButton");
ivjTextArea1.setText("MyText");
ivjTextArea1.setColumns(30);
ivjTextArea1.setRows(10);
this.setLayout(gridBagLayout1);
this.add(jButton1, new
GridBagConstraints(1,0,1,1,0.0,0.0
,GridBagConstraints.CENTER,
GridBagConstraints.NONE, new Insets(76,0,197,51),0,0));
this.add(jTextArea1,
new GridBagConstraints(0,0,1,1,1.0,1.0
,GridBagConstraints.CENTER,
GridBagConstraints.BOTH, new Insets(5,51,125,0),0,0));
}
}

The Visual Editor will not parse this code pattern to show the desired JPanel with the JTextArea and JButton because of the following differences between the source and the recognized code patterns.

The method that initializes the properties and relationships for the class being edited (the MyPanel class) is named jbInit(). For the Visual Editor it is named initialize() to be parsed correctly.

Before After
private void jbInit()
throws Exceptions{
private void initialize()
throws Exception{

The fields jButton1, jTextArea1 and gridBagLayout1 do not have a method that instantiates them, as they are constructed as part of the their field declarations. The instantiation code should be moved from the field declarations to the initialize() method. gridBagLayout1 is not a descendent of java.awt.Component, which usually requires its field to be prefixed with ivj. This is a Visual Editor parsing rule where fields that are typed to classes that implement java.awt.LayoutManager can have any name.

Before After
public class MyPanel extends JPanel{
private JButton jButton1 =
new JButton();
private JTextArea jTextArea1 =
new JTextArea();
private GridBagLayout
gridBagLayout1 = new
GridBagLayout()

public MyPanel(){
private void initialize()
throws Exception{
Button1.setText("MyButton");
...
public class MyPanel extends JPanel{
private JButton jButton1;
private JTextArea jTextArea1;
private GridBagLayout
gridBagLayout1

private void initialize()
throws Exception{
JButton1 = new JButton();
jTextArea1 = new JTextArea();
gridBagLayout1 = new
GridBagLayout()

jButton1.setText("MyButton");
...

The GridBagConstraints instance cannot be created during the argument to the method this.add(Component child,Object constraint); For the Visual Editor to parse the code correctly, a local method field should be used with the same initialization string that is passed to the method argument. Also, for GridBagConstraints, although the layout manager allows the same constraint instance to be shared across components, the Visual Editor expects a unique GridBagConstraints object to be created each time. In the following example, two new constraints are created, consB1 and consT1:

Before After
private void initialize()
throws Exception{
...
this.setLayout(gridBagLayout1);
this.add(jButton1, new
GridBagConstraints(1,0,1,1,0.0,0.0
,GridBagConstraints.CENTER,
GridBagConstraints.NONE ,new
Insets(76,0,197,51),0,0));
this.add(jTextArea1, new
GridBagConstraints(0,0,1,1,1.0,1.0
,GridBagConstraints.CENTER,
GridBagConstraints.BOTH , new
Insets(5,51,125,0),0,0));

}
private void initialize()
throws Exception{
...
this.setLayout(gridBagLayout1);
GridBagConstraints consB1 = new
GridBagConstraints(1,0,1,1,0.0,0.0 ,
GridBagConstraints.CENTER,
GridBagConstraints.NONE ,new
Insets(76,0,197,51),0,0);
this.add(jButton1,consB1);
GridBagConstraints consT1 = new
GridBagConstraints(0,0,1,1,1.0,1.0 ,GridBagConstraints.CENTER,
GridBagConstraints.BOTH , new
Insets(5,51,125,0),0,0)
this.add(jTextArea1,consT1);

}
}

After the following changes are made, the Visual Editor for Java can be used successfully with the MyPanel classes:

  • Rename the jbInit() method to initialize().
  • Move the instance field initialization to assignment expressions within the initialize() method.
  • Declare the GridBagConstraint arguments to add(Component,Object) to new unique method fields.

HelloWorldSwing sample

The following example shows a sample class HelloWorldSwing that creates a JFrame and adds a JLabel within the static void main(String[] args) method.

import javax.swing.*;
public class HelloWorldSwing {
public static void main(String[] args){
jFrame ivjFrame = new JFrame("HelloWorldSwing");
final JLabel ivjLabel = new JLabel("Hello World");
ivjFrame.getContentPane().add(ivjLabel);
ivjFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ivjFrame.pack();
ivjFrame.setVisible(true);
}
}
image010.jpg

The static main(String[] args) method is used by classes that are launched by the visual machine by being arguments to the java command. The Visual Editor does not use any special rules to parse the main(String[]) method. To be parsed, ivjFrame should be made an instance field:

Before After
import javax.swing.*;
public class HelloWorldSwing {
public static void main(String[] args){
JFrame ivjFrame = new
JFrame("HelloWorldSwing");

final JLabel ivjLabel = new
JLabel("Hello World");
ivjFrame.getContentPane().add(ivjLabel);
ivjFrame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
ivjFrame.pack();
ivjFrame.setVisible(true);
}
}
import javax.swing.*;
public class HelloWorldSwing {
static JFrame ivjFrame = null;
public static void main(String[] args){
ivyjFrame = new JFrame("HelloWorldSwing");
final JLabel ivjLabel = new
JLabel("Hello World");
ivjFrame.getContentPane().add(ivjLabel);
ivjFrame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
ivjFrame.pack();
ivjFrame.setVisible(true);
}
}

The contentPane of the JFrame is retrieved using getContentPane() and the JLabel is then added to it. However, the Visual Editor for Java does not support this implicit use of Java beans, and the contentPane must be declared as a field and assigned using a constructor. This means that any code that occurred in the JFrame during the creation of its contentPane will not occur, so if you have subclassed JFrame and put custom initialization logic for its contentPane, this should be moved out into a separate JPanel subclass that should be explicitly created and used as the contentPane.

Before After
public static void main(String[] args){
ivjFrame = new
JFrame("HelloWorldSwing");
final JLabel ivjLabel = new
JLabel("Hello World");
().add(ivjLabel);
ivjFrame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
ivjFrame.pack();
ivjFrame.setVisible(true);
}
}
public static void main(String[] args){
ivjFrame = new JFrame("HelloWorldSwing");
final JLabel ivjLabel = new JLabel("Hello World"); new
JPanel explicitContentPane = new JPanel();
ivjFrame.setContentPane(explicitContentPane);
explicitContentPane.add(ivjLabel);
ivjFrame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
ivjFrame.pack();
ivjFrame.setVisible(true);
}
}

Conclusion

This article has shown the rules by which the Visual Editor for Java interacts with Java source code. By understanding these rules and programming with them, you can make the Visual Editor recognize a wide number of source styles and convert code from other builders.


Questions from users

Question:
The article is very informative and exhaustive. I have one query which I would be grateful if you could solve. I have a Java bean with a property that I have declared as type Object. Actually I am trying to put in an array of String objects in it. In the initialize() method of a Java bean, if I try write textfield.setValue(new String[] {"ABC","XYZ"}) it gives the following error: java.lang.InitializationException. The expression is too complicated to be evaluated. In fact it gives this exception whenever I use any array in the setValue. Please advise. Thanks and regards, Paramjit Singh

Response from author:
Thanks for the feedback on the article. The Visual Editor for Java works by parsing the source and looking for recognized patterns that it can use to determine what the Java beans are, as well as their interrelationships and properties. For properties, the set method argument is analyzed and executed, so in your code the Visual Editor is detecting that the value property is the expression 'new String[] {"ABC","XYZ"}'. Because Java itself doesn't have an expression evaluator that we can use, the Visual Editor has to manually tokenize the expression to work out how to create the required object, in this case a String[] with two initial items. In some of our early releases we were unable to evaluate array expressions, but this was fixed before the V5.0 GA. Is it possible that you are running an early availability (EA) version? If the problem persists, please forward some sample source code together with screen shots.


About the authors

Joe Winchester is a member of the WebSphere Tools Development team working on the Visual Editor for Java for the Software Solutions group in Hursley, United Kingdom. You can reach Joe at winchest@uk.ibm.com).

Rich Kulp is an Advisory Software Engineer at IBM Raleigh Lab, North Carolina. He works on WebSphere development tools and is a member of the team working on the Visual Editor for Java. You can reach Rich at richkulp@us.ibm.com.

Dr. Gili Mendel is a Senior Advisory Software Engineer at IBM Raleigh Lab, North Carolina. He works on WebSphere development tools and is a member of the team working on the Visual Editor for Java. You can reach Gili at gmendel@us.ibm.com.

Peter Walker is an Advisory Software Engineer at IBM Raleigh Lab, North Carolina. He works on WebSphere development tools and is a member of the team working on the Visual Editor for Java. You can reach Peter at walkerp@us.ibm.com.

Srimanth Gunturi is a Staff Software Engineer at IBM Raleigh Lab, North Carolina. He works on WebSphere development tools and is a member of the team working on the Visual Editor for Java. You can reach Sri at sgunturi@us.ibm.com.

Jeffrey Myers is a member of the WebSphere Tools Development team working on the Visual Editor for Java for the Software Solutions group in Raleigh, North Carolina. Jeff can be reached at myersdj@us.ibm.com.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=13834
ArticleTitle=Source code patterns used by the Visual Editor for Java in WebSphere Studio V5
publish-date=02252003
author1-email=winchest@uk.ibm.com
author1-email-cc=
author2-email=
author2-email-cc=
author3-email=dwinfo@us.ibm.com
author3-email-cc=
author4-email=
author4-email-cc=
author5-email=
author5-email-cc=
author6-email=myersdj@us.ibm.com
author6-email-cc=