Scaffold small client application frameworkwww.meldcraft.com
Slacker Guide to Java Swing Application Development
Using the Scaffold small client application framework

ZipTool Application

One powerful feature of the Scaffold framework is facilitating incremental application development. This example shows the steps to compose ZipTool - an application that can read a zip file and display the file information in a table.

ZipTool6 Screen Shot

Step 1 - Run the Application ?!?!

Lets begin by running the application (yes, it is conventionally impossible to run a program before writing it, but Scaffold provides just enough framework to do so).

java -cp Scaffold.jar \
com.meldcraft.application.AppManager \
com.meldcraft.ziptool.ZipToolApplication

ZipTool1 Screen Shot

So, we get a scrunched up JFrame. Not very pretty, but a reasonable start given the amount of effort involved.

Note that closing the frame (e.g. by selecting the window manager close icon) exits the VM - a feature of the Scaffold framework.

Step 2 - Title and Splash Screen

OK, lets give it a title, frame icon and splash screen - certainly not critical items, but typical of professional applications, and perhaps inspirational for the developer.

The application will have three files:

Application Files
com/meldcraft/ziptool/ZipToolApplication.properties
com/meldcraft/ziptool/ZipToolApplication_Splash.png
com/meldcraft/ziptool/ZipToolApplication32.png
com/meldcraft/ziptool/ZipToolApplication.properties
Application.name = Zip Tool

For simplicity, lets put our application in a directory ZipTool, with the aforementioned files in the ZipTool/src subdirectory, and the compilation going into the ZipTool/classes subdirectory. This is typical for a project in an IDE such as Eclipse or a command line builder such as ant.

Compile the application (OK, it's just copying files at this point), and run it:

java -cp Scaffold.jar;ZipTool/classes \
com.meldcraft.application.AppManager \
com.meldcraft.ziptool.ZipToolApplication

ZipTool2 Splash Screen Shot

ZipTool2 Screen Shot

So, we still get a scrunched up JFrame, but it has a title of "Zip Tool", an appropriate frame icon (something other than the default java icon), and we see a splash screen quickly that sticks around for about a second before showing the frame.

Step 3 - Menus and Toolbar

Next, lets give the application some form by adding basic menu and toolbar items. We'll augment ZipToolApplication.properties, and add an image for the Open menu and toolbar items, aiOpen16.png (the "ai" prefix is short for "Action Icon"):

Application Files
com/meldcraft/ziptool/ZipToolApplication.properties
com/meldcraft/ziptool/ZipToolApplication_Splash.png
com/meldcraft/ziptool/ZipToolApplication32.png
com/meldcraft/ziptool/aiOpen16.png
com/meldcraft/ziptool/ZipToolApplication.properties
Application.name = Zip Tool

Application.mainmenu = File Help
Application.maintoolbar = Open

File.name = _File
File.items = Open | Exit

Open.name = _Open
Open.accelerator = control O

Exit.name = E_xit

Help.name = _Help
Help.items = About

About.name = _About

Again, compile and run the program:

ZipTool3 Screen Shot

Now, the frame is at least big enough to display our menu and toolbar items. Note that the menu and toolbar buttons are active (e.g. pressing Alt-F will activate the File menu and the buttons can be selected) but they are not functional (they don't actually do anything).

Step 4 - Table View

The final visual piece of the ZipTool is a table to display zip file contents. Naturally, we want a table that handles a large number of entries (has scrollbars), and has sortable columns.

The default Swing JTable can be made to have scrollbars and sortable columns, but the details of doing so really require Component level development. Therefore, the Scaffold framework provides more high level table component better suited to being integrated as an Application Element. In this case, the table is based on the SwingX JXTable.

The only modification is to ZipToolApplication.properties:

com/meldcraft/ziptool/ZipToolApplication.properties
Application.name = Zip Tool

Application.mainmenu = File Help
Application.maintoolbar = Open

File.name = _File
File.items = Open | Exit

Open.name = _Open
Open.accelerator = control O

Exit.name = E_xit

Help.name = _Help
Help.items = About

About.name = _About

Application.rootContent = ZipTable

ZipTable.baseResource = com.meldcraft.application.guix.SSXTablePane
ZipTable.Columns = Name, Modified, Size, Ratio, Packed, Path
ZipTable.Columns.weights = 210, 110, 45, 40, 45, 370
ZipTable.Column.Modified.renderer.format = date,yyyy/MM/dd HH:mm:ss
ZipTable.Column.Size.renderer.format = number,/1000;0.0;0: :K:M:G:T:P
ZipTable.Column.Ratio.renderer.format = number,0%
ZipTable.Column.Packed.renderer.format = number,/1000;0.0;0: :K:M:G:T:P

Compile and run the program. This time we need to add the Scaffold ScaffoldGUIX.jar for the table Application Element.

java -cp Scaffold.jar;ScaffoldGUIX.jar;swingx.jar;ZipTool/classes \
com.meldcraft.application.AppManager \
com.meldcraft.ziptool.ZipToolApplication

ZipTool4 Screen Shot

Woohoo! Already, we have something that looks like a real application.

As a side note, this type of incremental progress is quite powerful in larger organizations. E.g. in an agile/iterative/cross-functional group, the developer has implemented enough functionality now for QA to generate use cases and start testing - i.e. this UI describes a lot of what the application/user can do, and the full functionality can continue incrementally.

Step 5 - Action for Menu and Toolbar Buttons

Now we hook up the menu and toolbar buttons. Again, we only need to add some text to ZipToolApplication.properties

com/meldcraft/ziptool/ZipToolApplication.properties
Application.name = Zip Tool

Application.mainmenu = File Help
Application.maintoolbar = Open

File.name = _File
File.items = Open | Exit

Open.name = _Open
Open.accelerator = control O
Open.target = ZipChooser
Open.method = showOpenDialog

Exit.name = E_xit
Exit.target = Application
Exit.method = applicationClose

Help.name = _Help
Help.items = About

About.name = _About
About.actionClassName = com.meldcraft.application.swing.action.AboutAction

Application.rootContent = ZipTable

ZipTable.baseResource = com.meldcraft.application.guix.SSXTablePane
ZipTable.Columns = Name, Modified, Size, Ratio, Packed, Path
ZipTable.Columns.weights = 210, 110, 45, 40, 45, 370
ZipTable.Column.Modified.renderer.format = date,yyyy/MM/dd HH:mm:ss
ZipTable.Column.Size.renderer.format = number,/1000;0.0;0: :K:M:G:T:P
ZipTable.Column.Ratio.renderer.format = number,0%
ZipTable.Column.Packed.renderer.format = number,/1000;0.0;0: :K:M:G:T:P

ZipChooser.baseResource = com.meldcraft.application.guis.SSFileChooser
ZipChooser.fileFilters = ZipChooser.AllFileFilter, ZipFilter

ZipFilter.className = com.meldcraft.application.guis.filechooser.BasicFileFilter
ZipFilter.extensions = .zip
ZipFilter.description = Zip files (*.zip)

Compile and run the program. This time we add the Scaffold ScaffoldGUIS.jar for a file chooser that is more suitable for use as an Application Element.

java -cp Scaffold.jar;ScaffoldGUIX.jar;swingx.jar;ScaffoldGUIS.jar;\
ZipTool/classes com.meldcraft.application.AppManager \
com.meldcraft.ziptool.ZipToolApplication

ZipTool5 Screen Shot

Selecting menu Help | About shows brief information about the Zip Tool application. Activating the Open button (menu File | Open or toolbar Open) displays a file chooser. By default, only *.zip files can be selected, but there is a dropdown option to select any file (*.*).

Opening a file closes the file chooser, but the table does not list the file contents. That is the last piece of the application, to be accomplished in the next step.

Step 6 - Business Logic

Of course, at some point we do have to write code to accomplish something unique. What's missing is code to open a zip file, read the contents, and put that information in a model for the table. For this we add a ZipTool.java file.

Application Files
com/meldcraft/ziptool/ZipToolApplication.properties
com/meldcraft/ziptool/ZipToolApplication_Splash.png
com/meldcraft/ziptool/ZipToolApplication32.png
com/meldcraft/ziptool/aiOpen16.png
com/meldcraft/ziptool/ZipTool.java
com/meldcraft/ziptool/ZipTool.java
package com.meldcraft.ziptool;
import java.util.*;
import java.util.zip.*;

public class ZipTool
{
  public Object[] fileOpen(ZipFile zip)
  {
    Object[] ret = null;
    if (zip != null)
    {
      ZipEntry[] entries = (ZipEntry[])Collections.list(zip.entries())
                                                  .toArray(new ZipEntry[0]);
      ArrayList data = new ArrayList();
      for (int i=0 ; i < entries.length ; i++)
      {
        if (!entries[i].isDirectory())
        {
          HashMap map = new HashMap();
          String name = entries[i].getName();
          String path = null;
          int idx = name.lastIndexOf('/');
          if (idx != -1)
          {
            path = name.substring(0, idx+1);
            name = name.substring(idx+1);
          }
          map.put("Name", name);
          map.put("Modified", new Date(entries[i].getTime()));
          map.put("Size", new Long(entries[i].getSize()));
          map.put("Packed", new Long(entries[i].getCompressedSize()));
          map.put("Ratio", new Double((double)entries[i].getCompressedSize()/
                                      (double)entries[i].getSize()));
          map.put("Path", path);
          data.add(map);
        }
      }
      ret = new Object[]{zip, data};
    }
    return ret;
  }
}

In addition, we connect the actions (e.g. from selecting Open in the file chooser) to invoke the business logic (i.e. the ZipTool.fileOpen() method).

com/meldcraft/ziptool/ZipToolApplication.properties
Application.name = Zip Tool
target = ZipTool
ZipTool.className = com.meldcraft.ziptool.ZipTool

Application.mainmenu = File Help
Application.maintoolbar = Open

File.name = _File
File.items = Open | Exit

Open.name = _Open
Open.accelerator = control O
Open.target = ZipChooser
Open.method = showOpenDialog

Exit.name = E_xit
Exit.target = Application
Exit.method = applicationClose

Help.name = _Help
Help.items = About

About.name = _About
About.actionClassName = com.meldcraft.application.swing.action.AboutAction

Application.rootContent = ZipTable

ZipTable.baseResource = com.meldcraft.application.guix.SSXTablePane
ZipTable.Columns = Name, Modified, Size, Ratio, Packed, Path
ZipTable.Columns.weights = 210, 110, 45, 40, 45, 370
ZipTable.Column.Modified.renderer.format = date,yyyy/MM/dd HH:mm:ss
ZipTable.Column.Size.renderer.format = number,/1000;0.0;0: :K:M:G:T:P
ZipTable.Column.Ratio.renderer.format = number,0%
ZipTable.Column.Packed.renderer.format = number,/1000;0.0;0: :K:M:G:T:P

ZipChooser.baseResource = com.meldcraft.application.guis.SSFileChooser
ZipChooser.fileFilters = ZipChooser.AllFileFilter, ZipFilter

ZipFilter.className = com.meldcraft.application.guis.filechooser.BasicFileFilter
ZipFilter.extensions = .zip
ZipFilter.description = Zip files (*.zip)

Application.contextListeners = Open.invokeReturn=FileOpenInvoker,\
                 FileOpenInvoker.invokeReturn=ZipTableDataInvoker,TitleInvoker

FileOpenInvoker.method = fileOpen
FileOpenInvoker.method.arg = <target>
FileOpenInvoker.invokeTargetThread = newThread
FileOpenInvoker.busyCursor = true

ZipTableDataInvoker.target = ZipTable
ZipTableDataInvoker.method = setData
ZipTableDataInvoker.method.arg = <target>[1]

TitleInvoker.target = UIRoot
TitleInvoker.method = setTitle
TitleInvoker.method.arg = <propertyRef:Application.name> - <target>[0].name

Compile and run the program.

ZipTool6 Screen Shot

Now, selecting Open in the file chooser invokes the ZipTool.fileOpen(ZipFile) method. The data returned from that method is then passed to the table, and the filename returned is used to update the title of the ZipTool application.

Note that the only Java code written is pure business logic. The ZipTool.java class is a simple POJO suitable for client or server user - no ties to Swing, AWT, SwingWorker, etc. The entire GUI is defined using declarative syntax in a plain old Java properties file.

Furthermore, note that the zip file is opened asynchronously. While the file is being loaded, a busy cursor covers the ZipTool. Also note that the table is automagically updated correctly in the AWT Event Dispatch Thread - the Scaffold framework greatly simplifies the amount of effort required to work asynchronously. While threading still has to be managed, it is much less complicated and invasive than using SwingWorker or similar coding.

Step 7 - More Information, Please

This ZipTool application example probably leaves you with more questions than answers.

If you feel this is neat, but a little too convenient and contrived, then perhaps looking at a few more examples will prove more convincing of the power and general applicability of the Scaffold framework.

If you are more intrigued by how the framework works, then check out the gory details.

© 2007 Meldcraft Corporation