Comments
Richard Davies wrote: The UK has a good crop of technology pioneers in cloud computing - for example ElasticHosts, FlexiScale, Flexiant, OnApp - and also some strong government initiatives such as G-Cloud. We will have to see whether this kind of technical leadership converts into swift mass-market adoption or not.
Cloud Computing
Conference & Expo
November 2-4, 2009 NYC
Register Today and SAVE !..

2008 West
DIAMOND SPONSOR:
Data Direct
SOA, WOA and Cloud Computing: The New Frontier for Data Services
PLATINUM SPONSORS:
Red Hat
The Opening of Virtualization
GOLD SPONSORS:
Appsense
User Environment Management – The Third Layer of the Desktop
Cordys
Cloud Computing for Business Agility
EMC
CMIS: A Multi-Vendor Proposal for a Service-Based Content Management Interoperability Standard
Freedom OSS
Practical SOA” Max Yankelevich
Intel
Architecting an Enterprise Service Router (ESR) – A Cost-Effective Way to Scale SOA Across the Enterprise
Sensedia
Return on Assests: Bringing Visibility to your SOA Strategy
Symantec
Managing Hybrid Endpoint Environments
VMWare
Game-Changing Technology for Enterprise Clouds and Applications
Click For 2008 West
Event Webcasts

2008 West
PLATINUM SPONSORS:
Appcelerator
Get ‘Rich’ Quick: Rapid Prototyping for RIA with ZERO Server Code
Keynote Systems
Designing for and Managing Performance in the New Frontier of Rich Internet Applications
GOLD SPONSORS:
ICEsoft
How Can AJAX Improve Homeland Security?
Isomorphic
Beyond Widgets: What a RIA Platform Should Offer
Oracle
REAs: Rich Enterprise Applications
Click For 2008 Event Webcasts
In many cases, the end of the year gives you time to step back and take stock of the last 12 months. This is when many of us take a hard look at what worked and what did not, complete performance reviews, and formulate plans for the coming year. For me, it is all of those things plus a time when I u...
SYS-CON.TV
A GUI Painter Friendly Table Component
The principle of the column container

In the early days of Java, GUI forms were written, not drawn. They were created by writing code that instantiated components and added them to containers with various layout constraints. Then the program was run and the result could be admired. This way of working, WYGIWYG (what you get is what you get) was often quite fun, more often frustrating, and never very productive. Today we have a JavaBeans specification and integrated development environments (IDEs) with GUI painters. Some of these are doing really good jobs, considering the difficulties with layout managers and platform portability.

With most components, such as text fields and buttons, the principle of dropping them on the form, setting properties, and adding event listeners is quite sufficient. The JTable though is more problematic. It's just too complex to configure with simple property editors and also so common that you don't want to have to write a lot of code every time you use it.

You can drop a table in a JScrollPane and set a lot of properties on the JTable, but when it comes to adding and customizing columns, the GUI painter can't help you since the columns are not JavaBeans. One solution is for the GUI painter to provide an editor for the table model property, thereby letting you define columns and set a few attributes on them. However, I have never seen an editor that will allow you to customize the columns of the table with the same flexibility you have when you customize text fields on a form.

There is, however, a completely different way to go, which is the one I chose for the table component in our own class library DOI, called the DoiTable.

Design Time Behavior
The DoiTable doesn't have a table model property editor at all. In fact, when you drop it on the form it doesn't even look like a table. Instead, it behaves like a container during design time, and you fill it with columns by dropping DoiTableColumn components inside it. At runtime, though, it automatically converts itself to a JTable with all the column properties taken from the design time column components.

Figure 1 shows the design time look of a simple table with four columns. The screenshot is taken from the NetBeans form editor. When the designer drops a table on the form, it appears as a big rectangle. The designer can then give the table a label and activate tools for inserting and deleting rows by setting properties on the table. Note the "Table" label and small tool bar above the rectangle. The rectangle is the drop area for columns. During design time this area is an ordinary panel with a flow layout in which column components can be dropped and reordered. The columns must be instances of the DoiTableColumn class. If you accidentally drop some other type of component inside it, the drop area turns red.

A DoiTableColumn is a direct descendant of the DoiTextField class, which is the standard text field in the DOI library, overridden to change the design time appearance and add some properties and behavior that is specific to a table column. As you can see, I've tried to make the column components look a bit like the columns they will become at runtime. From the GUI painter's point of view, the table is just a container. Therefore, the painter will allow you to set properties on each individual column as if they were ordinary fields on a panel, which is exactly what they are, until you run the application. In Figure 1, one of the columns is selected so you can see the property sheet for it in the lower right pane. Note also that the column components retain their preferred size even if the table is too narrow to show them all on one line. The fourth column, "Logical", doesn't fit, so it's placed on a new row. This behavior is consistent with any other flow layout panel. Although I could have made them resize themselves to mimic the behavior of a JTable more closely, I decided against it to make the columns easier for the designer to work with.

This is basically how the table component presents itself to the designer. To the user, however, it looks just like a JTable in a JScrollPane, as shown in Figure 2. I'll shortly go into the details on how this conversion happens, but first a little bit about how the table component communicates with the GUI painter.

Adjusting the BeanInfo
Every JavaBean component that you can draw on a form must have a supporting BeanInfo object, which is an instance of a class that implements the java.beans.BeanInfo interface. The BeanInfo object is used by the GUI painter to determine which properties and events the bean has. Although it can be created automatically using introspection, it's usually written by the author of the bean. Writing such a class is outside the scope of this article, but there is one important feature that is often forgotten when BeanInfo classes are described: the "container delegate" property. At the time of writing it isn't even mentioned in the Java Tutorial. Without this property, all beans must fall into the following two categories:

  1. Component beans such as JTextField or JButton - you drop them in containers but you don't drop anything inside them.
  2. Simple container beans such as JPanel - they are initially empty and you can drop components inside them.
The GUI painter can tell them apart by treating empty containers as category 2 and all other beans as category 1. The DoiTable, however, falls into a third category. It isn't just a container, but a container that initially has a label, a tool bar, and an inner container for the columns. Without a special "trick" in the BeanInfo class, the GUI painter would think that the DoiTable is an ordinary component because it isn't empty and won't let you drop anything inside it. This is certainly not the behavior we want, so we must inform the GUI painter that it is a container and that it has a special place for dropping stuff. The following code excerpt from the DoiTableBeanInfo class shows how this is done:


 public BeanDescriptor getBeanDescriptor()
 {
  BeanDescriptor bd =
   new BeanDescriptor(itsBeanClass);

  bd.setName("DoiTable");
 bd.setValue("isContainer",
        Boolean.TRUE);
  bd.setValue("containerDelegate",
        "getColumnContainer");

  return bd;
 }

The method creates a BeanDescriptor, which is an object that contains basic properties about the bean. While some of these properties have dedicated methods such as setName, others are set using the generic setValue method. In the code above, the property isContainer is set to TRUE to tell the GUI painter that although this bean isn't empty, it is still a container. We also have to tell the GUI painter which method on our bean returns the inner container by setting the property containerDelegate to the name of the method. In the DoiTable case, the method is called getColumnContainer.

Converting to Runtime Behavior
When the application is run we obviously don't want the table to look like it does in the GUI painter. Instead we want the drop area, a.k.a. the column container, to convert itself to a real JTable. This conversion happens in the method addNotify, which is called automatically on every component when it is added to a displayable container. This method may be called several times, so we must make sure the table doesn't attempt to con-vert itself more than once. Also, we don't want it to convert itself at all when we are using the table in the GUI painter. To test for design time or runtime mode, there is a method in the java.beans.Beans class called isDesignTime. This method returns true when called from a com-ponent in a GUI painter, and false otherwise.

The first thing we need to do is implement the addNotify method:


 public void addNotify()
 {
  super.addNotify();
  commitColumnContainer();
 }

The first thing the method does is invoke the same method on the superclass to let it do whatever it needs to do, then it calls the method commitColumnContainer to do the real work. This method looks like:


 public void commitColumnContainer()
 {
  commitColumnContainer(false);
 }

As you can see, it doesn't do much; it just delegates to another method. The reason for this is that the other method has a parameter that allows the caller to force a conversion even if we are in design time. This is useful in certain circumstances, which I'll get back to later. For now we'll look at the first few lines of the "real" commitColumnContainer method:


 public void commitColumnContainer(
      boolean pForce)
 {
  if (!pForce && Beans.isDesignTime())
   return;
  if (itsColumnContainer == null)
   return;

The method starts by checking if a conversion should happen at all by testing the force parameter and calling the isDesignTime method. If these tests are passed, it goes on to check if the table has already been converted. The column container panel is created and added to the table by the constructor and removed when the conversion is completed. This means that if it is null, the table is already converted and the method returns immediately. Now the real conversion can be done. We start off by transferring all column beans from the column container into an internal array:


  int ccc =
   itsColumnContainer.getComponentCount();
  
  itsColumns = new DoiTableColumn[ccc];
  	for (int i = 0; i < ccc; ++i) {
   DoiTableColumn column =
    (DoiTableColumn)itsColumnContainer
     .getComponent(i)
   itsColumns[i] = column;
   column.setTable(this);
  }

Each column is given a reference back to the table using the setTable method of the DoiTableColumn class. This reference is used by the column to access various properties on the table that affect its behavior. Now it's time to get rid of the column container and replace it with a scroll pane:


  remove(itsColumnContainer);
  itsColumnContainer = null;
  itsScrollPane = new JScrollPane();
  add(itsScrollPane, BorderLayout.CENTER);

The scroll pane will eventually contain a JTable, but before we can create it we need a column model, the object used by Swing's JTable to represent its columns. A JTable can automatically create the column model based on its table model, but we don't want that because the DoiTableColumn objects contain much more information about the columns than is contained in an ordinary table model, e.g., preferred width in characters, resizability, label text. etc.

The below code creates a column model that contains column objects of Swing's TableColumn class, with relevant properties copied from the corresponding DoiTableColumn objects:


  TableColumnModel colmod =
   new DefaultTableColumnModel();

  for (int i = 0; i < ccc; ++i) {
   // Get the column bean. Skip if hidden.
   DoiTableColumn column = itsColumns[i];
   if (column.isHidden())
    continue;
   // Create a Swing column.
   TableColumn swingColumn =
    new TableColumn();
   // Copy properties.
   swingColumn.setHeaderValue(
    column.getLabelText();
   swingColumn.setResizable(
    column.isResizable();
   // Add to column model.
   colmod.addColumn(swingColumn);
  }

There is still one little detail before we can create the JTable. We need a table model. A JTable can't exist without a table model so we need to create one that is initially empty. This is accomplished with the following code:


  TableModel tm =
   new DefaultTableModel(0, ccc);
Now the JTable can be created and added to the scroll pane that has replaced the column container. We also tell it not to automatically create a new column model if the table model is replaced later:


  JTable jt = new JTable(tm, colmod);
  jt.setAutoCreateColumnsFromModel(false);

  itsScrollPane.add(jt);

That's it. The DoiTable bean now contains a JTable within a JScrollPane instead of a column container panel. The DoiTableColumn beans still exist though, and there is an implicit association between each column bean with the corresponding Swing TableColumn object in the column model. This association will prove very useful for later enhancements, some of which I'll hint at in the next section.

I promised to mention the purpose of the pForce parameter. This parameter can be used by subclasses of the DoiTable that create and add all columns. Let's say you want to create a bean called PhoneNumberTable, with a number type column and a phone number column. This bean would add its columns in the constructor and then call commitColumnCon-tainer(true) to force the conversion to a JTable. In this case, the force parameter is necessary since the conversion must happen in design time as well as runtime.

Enhancements
The purpose of this article is to show you the principle of the column container, not how to write a full-fledged table component. To do that, I'd probably have to fill 10 issues of JDJ. For this reason the code examples shown of what really happens inside the DoiTable have been simplified. Still, I'd like to round off with a brief list of some interesting features in the real DOI classes.

Runtime Propagation of Properties
Many of the DoiTableColumn properties are automatically propagated to the JTable when changed at runtime. This allows runtime code to dynamically change the table by simply setting properties on the DoiTableColumn bean, which is much easier than doing it through the JTable. For example, the column header is updated if the label text of the column is set. This propagation is accomplished through the implicit association between the invisible column bean and the visible table column.

Runtime Synchronization of Cell Values
The DoiTable has a property called ContextRowNo that can be set programmatically. It is also updated automatically when the user selects a row. I mentioned earlier that the DoiTableColumn class is a subclass of a class called DoiTextField, which is an enhancement of JTextField. This means that a DoiTableColumn bean can have a value. The context row is used to synchronize the value of a column bean and the corresponding cell value. The designer can add a listener on a column bean that's triggered when the user edits the cell. The event handler can then access the cell value through the column bean and set a value on another cell on the same row, also through a column bean. The code for this is easier to write and maintain than using a table model listener.

Design Time Rendering
As you can see in Figure 1, the text fields in the column beans are not empty. Instead they contain a text value that reflects a few important properties (a feature inherited from the base class DoiTextField): a mandatory column has an exclamation mark suffix, a numeric column is displayed with "#", "##" or "#.#" (depending on if it is an Integer, Long, or Double), an uppercase string column uses "ABC", etc.

Smart Design Time Checking
In some circumstances, checking for design-time mode is not sufficient. Some IDEs, for example, NetBeans, have a preview function that creates a window with the form inside it where the designer can try it out. The isDesignTime method still returns true, however, which causes DoiTable to think that it's still in design mode, and it doesn't convert itself. To get around this, it has its own isDesignTime method that first calls the standard method. If it returns false we are in "real" runtime, and no further checking is necessary; if it returns false an extra check for the special preview mode is necessary. This check is IDE dependent, and in the NetBeans case it is done by searching the parent container hierarchy for the innermost frame that has a title starting with "Testing Form[". Other IDEs will most likely need variations of this technique.

Conclusion
I hope I've provided you with some ideas that you can use when you write your own beans. The same principle can naturally be applied to very different kinds of widgets, especially complex ones that are easier to design with if they are broken up into parts. The time spent on doing this is earned many times over when the end-user GUIs are designed.

Resources

  • The Java Tutorial, trail JavaBeans: java.sun.com/docs/books/tutorial/javabeans/index.html
  • NetBeans FAQ - GUI Editing: www.netbeans.org/kb/faqs/gui_editing.html
  • About Gunnar Grim
    Gunnar Grim is a programmer, designer, and architect for the consulting firm Know IT (www.knowit.se). He has been in the business for 20 years, programming in everything from Z80 assembly code to SQL Windows. Since early 1996 he has worked almost exclusively with Java, mostly on the server side but also quite a lot with Swing.

    In order to post a comment you need to be registered and logged in.

    Register | Sign-in

    Reader Feedback: Page 1 of 1

    SOA World Latest Stories
    In a surprise move on Tuesday, January 10, Oracle wheeled out its Big Data Appliance. That’s the one it said in October would be ready sometime in the first half. Only nobody believed it meant early in the first half. Heck, it’s not even clear anybody thought Oracle could make the fi...
    A Munich court Thursday found Motorola Mobility guilty of infringing an Apple patent and handed Apple a permanent injunction against two Android smartphones. Apple can enforce the injunction after posting a bond lest MMI succeed in invalidating the slide-to-unlock patent (EP1964022) ...
    Quick Response (QR) codes are intended to help direct users quickly and easily to information about products and services, but they are also starting to be used for social engineering exploits. This article looks at the emergence of QR scan scams and the rising concern for users today....
    The Chinese company that claims it owns the iPad trademark says it plans to seek a ban on iPad exports out of China, threatening global supplies. According to what a lawyer for Proview Technology (Shenzhen) Co Ltd told Reuters, the firm is petitioning Chinese customs to stop shipment...
    Cisco Wednesday filed suit in the European Union’s second-highest court, the General Court in Luxembourg, challenging the European Commission’s rubber stamp last October of Microsoft’s $8.5 billion acquisition of Skype. Cisco says it isn’t opposed to the merger, but figures the EC sh...
    2011 was a year of rapid adoption for public and private cloud services. Instant and on-demand server provisioning was the driving force behind the massive growth. On top, cloud server templates and script automation simplified application installation for simple and pre-defined applic...
    Subscribe to the World's Most Powerful Newsletters
    Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
    Click to Add our RSS Feeds to the Service of Your Choice:
    Google Reader or Homepage Add to My Yahoo! Subscribe with Bloglines Subscribe in NewsGator Online
    myFeedster Add to My AOL Subscribe in Rojo Add 'Hugg' to Newsburst from CNET News.com Kinja Digest View Additional SYS-CON Feeds
    Publish Your Article! Please send it to editorial(at)sys-con.com!

    Advertise on this site! Contact advertising(at)sys-con.com! 201 802-3021


    SYS-CON Featured Whitepapers
    ADS BY GOOGLE