Welcome!

Java IoT Authors: Elizabeth White, Pat Romanski, Liz McMillan, Yeshim Deniz, Frank Lupo

Related Topics: Java IoT

Java IoT: Article

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
  • More Stories By 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.

    Comments (0)

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


    @ThingsExpo Stories
    SYS-CON Events announced today that Avere Systems, a leading provider of hybrid cloud enablement solutions, will exhibit at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. Avere Systems was created by file systems experts determined to reinvent storage by changing the way enterprises thought about and bought storage resources. With decades of experience behind the company’s founders, Avere got its ...
    High-velocity engineering teams are applying not only continuous delivery processes, but also lessons in experimentation from established leaders like Amazon, Netflix, and Facebook. These companies have made experimentation a foundation for their release processes, allowing them to try out major feature releases and redesigns within smaller groups before making them broadly available. In his session at 21st Cloud Expo, Brian Lucas, Senior Staff Engineer at Optimizely, will discuss how by using...
    In this strange new world where more and more power is drawn from business technology, companies are effectively straddling two paths on the road to innovation and transformation into digital enterprises. The first path is the heritage trail – with “legacy” technology forming the background. Here, extant technologies are transformed by core IT teams to provide more API-driven approaches. Legacy systems can restrict companies that are transitioning into digital enterprises. To truly become a lead...
    SYS-CON Events announced today that Yuasa System will exhibit at the Japan External Trade Organization (JETRO) Pavilion at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. Yuasa System is introducing a multi-purpose endurance testing system for flexible displays, OLED devices, flexible substrates, flat cables, and films in smartphones, wearables, automobiles, and healthcare.
    SYS-CON Events announced today that CAST Software will exhibit at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 - Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. CAST was founded more than 25 years ago to make the invisible visible. Built around the idea that even the best analytics on the market still leave blind spots for technical teams looking to deliver better software and prevent outages, CAST provides the software intelligence that matter ...
    SYS-CON Events announced today that Daiya Industry will exhibit at the Japanese Pavilion at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. Ruby Development Inc. builds new services in short period of time and provides a continuous support of those services based on Ruby on Rails. For more information, please visit https://github.com/RubyDevInc.
    SYS-CON Events announced today that Evatronix will exhibit at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. Evatronix SA offers comprehensive solutions in the design and implementation of electronic systems, in CAD / CAM deployment, and also is a designer and manufacturer of advanced 3D scanners for professional applications.
    As businesses evolve, they need technology that is simple to help them succeed today and flexible enough to help them build for tomorrow. Chrome is fit for the workplace of the future — providing a secure, consistent user experience across a range of devices that can be used anywhere. In her session at 21st Cloud Expo, Vidya Nagarajan, a Senior Product Manager at Google, will take a look at various options as to how ChromeOS can be leveraged to interact with people on the devices, and formats th...
    SYS-CON Events announced today that Taica will exhibit at the Japan External Trade Organization (JETRO) Pavilion at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. Taica manufacturers Alpha-GEL brand silicone components and materials, which maintain outstanding performance over a wide temperature range -40C to +200C. For more information, visit http://www.taica.co.jp/english/.
    SYS-CON Events announced today that SourceForge has been named “Media Sponsor” of SYS-CON's 21st International Cloud Expo, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. SourceForge is the largest, most trusted destination for Open Source Software development, collaboration, discovery and download on the web serving over 32 million viewers, 150 million downloads and over 460,000 active development projects each and every month.
    Enterprises have taken advantage of IoT to achieve important revenue and cost advantages. What is less apparent is how incumbent enterprises operating at scale have, following success with IoT, built analytic, operations management and software development capabilities – ranging from autonomous vehicles to manageable robotics installations. They have embraced these capabilities as if they were Silicon Valley startups. As a result, many firms employ new business models that place enormous impor...
    SYS-CON Events announced today that TidalScale will exhibit at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. TidalScale is the leading provider of Software-Defined Servers that bring flexibility to modern data centers by right-sizing servers on the fly to fit any data set or workload. TidalScale’s award-winning inverse hypervisor technology combines multiple commodity servers (including their ass...
    As popularity of the smart home is growing and continues to go mainstream, technological factors play a greater role. The IoT protocol houses the interoperability battery consumption, security, and configuration of a smart home device, and it can be difficult for companies to choose the right kind for their product. For both DIY and professionally installed smart homes, developers need to consider each of these elements for their product to be successful in the market and current smart homes.
    SYS-CON Events announced today that MIRAI Inc. will exhibit at the Japan External Trade Organization (JETRO) Pavilion at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. MIRAI Inc. are IT consultants from the public sector whose mission is to solve social issues by technology and innovation and to create a meaningful future for people.
    In his Opening Keynote at 21st Cloud Expo, John Considine, General Manager of IBM Cloud Infrastructure, will lead you through the exciting evolution of the cloud. He'll look at this major disruption from the perspective of technology, business models, and what this means for enterprises of all sizes. John Considine is General Manager of Cloud Infrastructure Services at IBM. In that role he is responsible for leading IBM’s public cloud infrastructure including strategy, development, and offering ...
    As hybrid cloud becomes the de-facto standard mode of operation for most enterprises, new challenges arise on how to efficiently and economically share data across environments. In his session at 21st Cloud Expo, Dr. Allon Cohen, VP of Product at Elastifile, will explore new techniques and best practices that help enterprise IT benefit from the advantages of hybrid cloud environments by enabling data availability for both legacy enterprise and cloud-native mission critical applications. By rev...
    SYS-CON Events announced today that Dasher Technologies will exhibit at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 - Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. Dasher Technologies, Inc. ® is a premier IT solution provider that delivers expert technical resources along with trusted account executives to architect and deliver complete IT solutions and services to help our clients execute their goals, plans and objectives. Since 1999, we'v...
    SYS-CON Events announced today that NetApp has been named “Bronze Sponsor” of SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. NetApp is the data authority for hybrid cloud. NetApp provides a full range of hybrid cloud data services that simplify management of applications and data across cloud and on-premises environments to accelerate digital transformation. Together with their partners, NetApp emp...
    SYS-CON Events announced today that TidalScale, a leading provider of systems and services, will exhibit at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 - Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. TidalScale has been involved in shaping the computing landscape. They've designed, developed and deployed some of the most important and successful systems and services in the history of the computing industry - internet, Ethernet, operating s...
    Join IBM November 1 at 21st Cloud Expo at the Santa Clara Convention Center in Santa Clara, CA, and learn how IBM Watson can bring cognitive services and AI to intelligent, unmanned systems. Cognitive analysis impacts today’s systems with unparalleled ability that were previously available only to manned, back-end operations. Thanks to cloud processing, IBM Watson can bring cognitive services and AI to intelligent, unmanned systems. Imagine a robot vacuum that becomes your personal assistant tha...