JustNuff

This article was originally published in VSJ, which is now part of Developer Fusion.
JustNuff is designed specifically for developers with the need for "just enough" GUI, and promises to deliver ease of use, efficiency of resource usage, leverage of the latest JDK 1.4 GUI elements, and extensibility. This month, we investigate how the JustNuff library satisfies these requirements. Along the way, we'll present some advanced Swing library programming tips, as well as some useful techniques when creating Java API libraries.

The UISystem.java file contains the code for the singleton instance – the object instance used to make all the API calls. A private constructor ensures that users cannot instantiate additional instances via the new operator. The static getInstance() method must be used to obtain the singleton instance:

public final class UISystem {
	private static final UISystem INSTANCE = new UISystem();
	...
	private UISystem() {
		}
	public static UISystem getInstance() {
		return INSTANCE;
		}

The versatile JOptionPane

The first four status modal dialog methods are actually thin wrappers around the static methods provided by the JOptionPane in the swing library. The JOptionPane class is a class for displaying different styles of managed dialog boxes.

JustNuff Method JOptionPane Method
showInformationModalDialog showMessageDialog
showWarningModalDialog showMessageDialog
showErrorModalDialog showMessageDialog
showYesNoCancelModalDialog showConfirmDialog

The first three methods above are all slightly different parameterisation of the option pane's showMessageDialog() method. For example, the showErrorModalDialog() method has the following code:

public void showErrorModalDialog(
	String inText) throws Exception {
	final String tpText = inText;
	Runnable runSeg = new Runnable() {
		public void run() {
		JOptionPane.showMessageDialog(
		null, tpText, "Error",
		JOptionPane.ERROR_MESSAGE);
			} };
	EventQueue.invokeAndWait(runSeg);
	}
The first null parameter indicates that this modal dialog has no specified parent GUI element. The swing library will then create a shared invisible frame to act as its parent. Invocation of this static method is quite efficient since the swing library manages the instances of dialog objects that are created, and will reuse instances.

Note the use of an anonymous Runnable class to bracket the call into the swing library. This is the mechanism we have discussed earlier to synchronise between the application logic and the GUI subsystem.

Making thread-safe Swing calls

Because Swing is not a thread-safe library, it is very important that we ensure that the GUI manipulations that we perform are done via the one and only GUI event handling thread. By creating a Runnable anonymous class that implements our actions, the swing API calls that we make can be turned into an event for the GUI event handling thread to execute. When we call the invokeAndWait method from the event queue, we will block execution until this body of code (that is everything inside the run() method of the Runnable interface) is executed. This will ensure the actions are performed by the event handling thread, thus honouring the thread-safety requirement of the Swing library. You will see this construct throughout the JustNuff library. This construct ensures that we satisfy Requirement 2 as listed in last month's article, that is:
  • All library APIs must be callable from an application logic thread

Figure 1
Figure 1

Figure 1 illustrates the interaction flow. It should be clear that all GUI work will be confined to the GUI handler thread, and that the JustNuff API calls can be made at any time from the application logic thread without thread-safety problems.

Using JOptionPane to obtain input

To create the showTextInputModalDialog() method, we can wrap JOptionPane's static showInputDialog() method:
private String strRetVal = null;
public String
	showTextInputModalDialog(String
	inText) throws Exception {
	final String tpText = inText;
	Runnable runSeg = new Runnable() {
		public void run() {
			strRetVal =
	JOptionPane.showInputDialog(
			null, tpText);
			} };
	EventQueue.invokeAndWait(runSeg);
	return strRetVal;
	}
Note that the shared variable here, strRetVal, is assigned a value by the GUI handler thread while the application thread is blocked. After the GUI handler thread completes its operation, the application thread is unblocked and strRetVal is then accessed from the application logic thread and returned to the caller.

Creating custom dialogs

While the static methods in JOptionPane handle the display and management of status message boxes and basic text string input well, they fall short if we need any additional customisation and functionality. If we need an input dialog for (say) a phone number or a date, then we are left to our own devices to come up with a solution.

The easiest, and compatible, way to accomplish this is via the composition of JOptionPane. To show how we can create our own custom modal dialogs by embedding an instance of JOptionPane, the JustNuff library contains showNumericInputModalDialog that return a long typed value. This method uses a custom NumericInputModalDialog class. Figure 2 illustrates the construction of this custom NumericInputModalDialog class.

Figure 2
Figure 2

In our code, we need to perform the following steps:

  1. subclass JDialog
  2. set the parent to behave in a modal manner
  3. create a custom text field that only return valid long typed value
  4. create a customised instance of JOptionPane
  5. set the content pane of the JDialog with the customised JOptionPane instance, enabling the content pane to control the appearance and interaction of the dialog
  6. handle the property change event from button click
  7. transfer the data from the custom text field to the return value when OK is clicked
  8. hide the dialog (for reuse!) and clear the text field if CANCEL is clicked or if the box is closed
  9. provide custom cleanup logic when the dialog is closed
  10. provide public accessor methods to the prompt message, and the return value
The listing below is for NumericInputModalDialog.java, and has each of the above steps labelled for easy reference. You will have noticed that we've used a JFormattedTextField for the input of numeric value. We will see how to use this new JDK 1.4 Swing component shortly.
package uk.co.vsj.ui.justnuff;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JLabel;
import javax.swing.JFormattedTextField;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.NumberFormat;
1final class NumericInputDialog extends JDialog
	implements PropertyChangeListener {
	private long numericValue = 0;
	private JFormattedTextField textField;
	private JOptionPane optionPane;
	private String okButtonText = "OK";
	private String cancelButtonText = "Cancel";
	private NumberFormat numberFormat =
		NumberFormat.getNumberInstance();
	private JLabel messageLabel;
	public NumericInputDialog(Frame parentFrame,
		String promptMsg ) {
	2	super(parentFrame, true);
		setTitle("Please enter a number...");
		messageLabel = new JLabel(promptMsg);
	3	textField = new
			JFormattedTextField(numberFormat);
		Object[] array = {messageLabel, "", textField};
		Object[] options = {okButtonText,
			cancelButtonText};
		//Create the JOptionPane.
	4	optionPane = new JOptionPane(array,
			JOptionPane.QUESTION_MESSAGE,
			JOptionPane.YES_NO_OPTION, null, options,
			options[0]);
	5	setContentPane(optionPane);
		setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent we) {
				optionPane.setValue(new Integer(
				JOptionPane.CLOSED_OPTION));
				}
			});
	6	optionPane.addPropertyChangeListener(this);
		}
6	public void propertyChange(PropertyChangeEvent e) {
		String prop = e.getPropertyName();
		if (isVisible()
			&& (e.getSource() == optionPane)
			&& (JOptionPane.VALUE_PROPERTY.equals(prop) ||
			JOptionPane.INPUT_VALUE_PROPERTY.equals(prop))) {
			Object value = optionPane.getValue();
			if (value == JOptionPane.UNINITIALIZED_VALUE) {
				//ignore reset
				return;
				}
			optionPane.setValue(
				JOptionPane.UNINITIALIZED_VALUE);
			if (okButtonText.equals(value)) {
			7	try {
					numericValue = ((Long)
						textField.getValue()).longValue();
				} catch (Exception ex) {
					// should never happen
					ex.printStackTrace();
					numericValue =0;
					}
				clearAndHide();
				} else { //user closed dialog or clicked cancel
			8	numericValue = 0;
				clearAndHide();
				}
			}
		}
9	private void clearAndHide() {
		textField.setText(null);
		setVisible(false);
		}
10	public void setPromptMessage(String inText) {
		messageLabel.setText(inText);
	}

10	public void setNumericValue(long inVal) {
		numericValue = inVal;
		textField.setText(String.valueOf(inVal));
		textField.selectAll();
		}
10	public long getNumericValue() {
		return numericValue;
		}
	}

Meeting design requirements

One of our design requirements is to minimise the number of objects created, another is that instances of complex dialog should be reused. The showNumericInputModalDialog() achieves this by reusing a singleton static instance of a NumericInputModalDialog(). The following code segment illustrates how this is done:
private static final NumericInputDialog numDlg =
		new NumericInputDialog(null, "");
...
private long longRetVal = 0;
public long showNumericInputModalDialog(String inText)
	throws Exception {
	final String tpText = inText;
	Runnable runSeg = new Runnable() {
		public void run() {
			numDlg.setPromptMessage(tpText);
			numDlg.setNumericValue(0);
			numDlg.pack();
			numDlg.setVisible(true);
			longRetVal = numDlg.getNumericValue();
			} };
	EventQueue.invokeAndWait(runSeg);
	return longRetVal;
	}
If you examine the code of showDateInputModalDialog() and showPhoneInputModalDialog(), you will see the same technique used to reuse the singleton static instances of DateInputModalDialog and PhoneInputModalDialog respectively.

Refactoring

Another design requirement is to ensure that the library can be easily extended. Since we cannot possibly provide enough customised dialogs for every user of the library, it is a good idea to make it simpler to create new customised dialog classes. The code of NumericInputDialog is quite intricate, and can be error prone because it combines both dialog handling code and field and data management code. Since every custom dialog has the same pair of buttons, has some fields for users to enter data, and has to transfer data into the return value when the "OK" button is pressed, it is possible to refactor the logic of NumericInputDialog and create a base class for all custom dialogs. This class is called CustomInputDialog in the JustNuff library, and it is declared abstract since it is designed to be extended and not instantiated directly. The listing below is the source from NumericInputDialog, annotated with the steps that we saw in the NumericInputDialog custom logic. Note that step 3 is missing, steps 7–9 are calls on abstract functions, and step 10 is also partially missing. These are the precise steps that will be supplied by the subclass of CustomInputDialog since they differ for each custom dialog.
package uk.co.vsj.ui.justnuff;
import javax.swing.JOptionPane;
import javax.swing.JDialog;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
1public abstract class CustomInputDialog extends
	JDialog implements PropertyChangeListener {
	private String typedText = null;
	private JOptionPane optionPane;
	private String okButtonText = "OK";
	private String cancelButtonText = "Cancel";
	protected CustomInputDialog(Frame parentFrame) {
	2	super(parentFrame, true);
		}
4	protected void createPane(Object [] array) {
		//Create the JOptionPane.
		Object[] options = {okButtonText, cancelButtonText};
		optionPane = new JOptionPane(array,
			JOptionPane.QUESTION_MESSAGE,
			JOptionPane.YES_NO_OPTION, null, options,
			options[0]);
		//Make this dialog display it.
	5	setContentPane(optionPane);
		setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent we) {
				optionPane.setValue(new Integer(
				JOptionPane.CLOSED_OPTION));
				}
			});
	6	optionPane.addPropertyChangeListener(this);
		}
6	public void propertyChange(PropertyChangeEvent e) {
		String prop = e.getPropertyName();
		if (isVisible() && (e.getSource() == optionPane)
			&& (JOptionPane.VALUE_PROPERTY.equals(prop) ||
			JOptionPane.INPUT_VALUE_PROPERTY.equals(prop))) {
			Object value = optionPane.getValue();
			if (value == JOptionPane.UNINITIALIZED_VALUE) {
				//ignore reset
				return;
				}
			optionPane.setValue(
				JOptionPane.UNINITIALIZED_VALUE);
			if (okButtonText.equals(value)) {
			7	afterOKButtonClick();
				clearAndHide();
			} else { //user closed dialog or clicked cancel
			8	afterCancelButtonClick();
				clearAndHide();
				}
			}
		}
9	protected void clearAndHide() {
		beforeClosing();
		setVisible(false);
		}
	protected abstract void afterOKButtonClick();
	protected abstract void afterCancelButtonClick();
	protected abstract void beforeClosing();
10	public abstract void
		setPromptMessage(String inText);
	}
Subclassing from CustomInputDialog can make extending the JustNuff library quite simple. This is how we constructed DateInputModalDialog (for input of date typed data) and PhoneInputModalDialog (for input of phone number typed data), and how you can create your own specialised input dialog class.

Using the JDK 1.4 GUI

One of the requirements of our JustNuff library was to use some of the new GUI features in JDK 1.4, in order to give a current and contemporary feel to the GUI. The fact that one of these new features actually simplifies the coding of the library is a big plus. The JDK 1.4 enhancement that we will exploit is JFormattedTextField. JFormattedTextField is a subclass of JTextField, and it provides the ability to format data displayed in the field and/or filter data being input into the field.

A complete detailed discussion of the MVC (model-controller-view) architecture used by JFormattedTextField is beyond the scope of this article. Interested readers are encouraged to go through the JavaDoc documentation and tutorial supplied with JDK 1.4. For our purposes, it is adequate to know that one can specify the data formatter to be used by JFormattedTextField when the field is instantiated. More concretely, the following code can be used to obtain input into a date typed data field:

private SimpleDateFormat dateFormat
= new SimpleDateFormat("MM/dd/yyyy");
...
textField = new
JFormattedTextField(dateFormat );
The above code will return the input data in month/day/year format, as specified by the instance of SimpleDateFormat. As of JDK 1.4, however, it will not restrict input to the specific date format.

Also the following code can be used to obtain a phone-formatted value from a JFormattedTextField:

MaskFormatter mformat = null;
try {
	mformat = new
	MaskFormatter("(###) ###-####");
	phoneField = new
	JFormattedTextField(mformat);
	}
catch (java.text.ParseException ex) {
	... }
A MaskFormatter instance can be used to filter and explicitly restrict the input of data according to a specified mask. Some of the valid characters in the mask specification are tabulated below.

Mask char Purpose
# Any digit 0–9
U Any letter, input converted to upper case
L Any letter, input converted to lower case
A Any letter or digit
? Any character, no case conversion
* Anything – unrestricted character
H Any hex digit
' Escape any mask character
Other chars Reproduce the character exactly

In the NumericInputDialog, we have already seen the code that obtains input of a numeric typed data:

private NumberFormat numberFormat =
	NumberFormat.getNumberInstance();
...
textField = new
	JFormattedTextField(numberFormat);

Creating new dialog types

Let's take a look, then, at how the PhoneInputDialog takes advantage of the CustomInputDialog abstract class as well as the JDK 1.4 JFormattedTextField. The code of PhoneInputDialog is presented below, and the corresponding steps (from the earlier dialog list) are labelled. Compare this to the coding of NumericInputDialog and you will appreciate how the CustomInputDialog abstract class saves significant work when creating new custom dialogs.
package uk.co.vsj.ui.justnuff;
import java.util.Date;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import java.awt.Frame;
import java.text.SimpleDateFormat;
final class DateInputDialog extends CustomInputDialog {
	private Date dateValue = null;
	private JFormattedTextField textField;
	private JLabel messageLabel;
	private SimpleDateFormat dateFormat =
		new SimpleDateFormat("MM/dd/yyyy");
	public DateInputDialog(Frame parentFrame,
		String promptMsg ) {
		super(parentFrame);
		messageLabel = new JLabel(promptMsg);
		System.err.println("in constructor");
		setTitle("Please enter date...");
	3	textField = new JFormattedTextField(dateFormat );
		textField.setValue(new Date());
		Object[] array = {messageLabel, "", textField};
		createPane(array);
		}
7	protected void afterOKButtonClick() {
		try {
			dateValue = dateFormat.parse(textField.getText());
		} catch (Exception ex) {
			dateValue = null;
			}
		}
8	protected void afterCancelButtonClick() {
		System.err.println("other branch entered..." );
		dateValue = null;
		}
9	protected void beforeClosing() {
		textField.setValue(null);
		}
10	public void setPromptMessage(String inText) {
		messageLabel.setText(inText);
		}
10	public void setInputDate(Date inDate) {
		dateValue = inDate;
		textField.setText(dateFormat.format(dateValue));
		textField.selectAll();
		}
10	public Date getInputDate() {
		return dateValue;
		}
	}
Of course, the JustNuff library's UISystem class manages the singleton DateInputDialog used. This code is similar to that of the showNumericInputModalDialog() method:
private static final DateInputDialog
dateDlg = new DateInputDialog(
null, "");
...
private Date dateRetVal;
public Date showDateInputModalDialog(
String inText) throws Exception {
	final String tpText = inText;
	Runnable shownDialog =
		new Runnable() {
		public void run() {
	dateDlg.setPromptMessage(tpText);
	dateDlg.setInputDate(new Date());
	dateDlg.pack();
	dateDlg.setVisible(true);
dateRetVal = dateDlg.getInputDate();
			} };
EventQueue.invokeAndWait(shownDialog);
	return dateRetVal; 	}

Handling object input

An ObjectInputDialog will take an object as input and generate on-the-fly a dialog that can be used to obtain values for each public field of the object. This is a versatile feature that is implemented using the reflection API. The reflection API enables us to discover the details of any class at runtime.

Here is how ObjectInputDialog does its work. First, there is a helper class called FieldWrapper. This class associates the field name with the GUI text field that contains its value. It also has two helper methods, one to transfer data from the text field into the object, and the other one for clearing up the object's field value:

class FieldWrapper {
	JFormattedTextField _intField;
	String _fieldName;
	public FieldWrapper(String inName,
		JFormattedTextField inField) {
		_fieldName = inName;
		_intField = inField; }
xferData() moves data from the GUI text field to the object. Note the use of Field.set() method that works as long as the type of the second calling argument is the same as the type of the field:
public void xferData(Object inObj) {
	Class objClass = inObj.getClass();
	Field curField = null;
	try {
		curField = objClass.getField(
			_fieldName);
	} catch
	(NoSuchFieldException ex) {
		// should never occur
		ex.printStackTrace(System.err);
		curField = null;
		// force assert
		}
	assert curField != null;
	Object curValue =
		_intField.getValue();
	if (curValue != null) {
		try {
			curField.set(inObj,
				_intField.getValue()
				);
			}
	catch (IllegalAccessException ex)
		{
// should never happen because we
// already accessed fields earlier
		ex.printStackTrace(System.err);
		} } }
The zapData() method cleans up the value of the GUI field:
	public void zapData() {
		_intField.setValue(null);
		} }
ObjectInputDialog extends CustomInputDialog to simplify coding:
class ObjectInputDialog extends
CustomInputDialog {
	private String typedText = null;
	private JFormattedTextField
		textField;
	private JLabel messageLabel;
The fieldsArray will be used to hold the object's public data field to GUI field mapping information, there will be one entry for each public data field in the object. The object itself is stored in the inputObject private variable:
private FieldWrapper [] fieldsArray;
private Object inputObject = null;
public ObjectInputDialog(Frame
	parentFrame, String promptMsg ,
	Object inObj) {
	super(parentFrame);
	inputObject = inObj;
	messageLabel = new
		JLabel(promptMsg);
	System.err.println("in
		ObjectInputDialog
		constructor for class " +
		inObj.getClass().getName());
	setTitle("Please Enter Data...");
Consider the incoming object. First we get its class, then use the class to get the fields:
Class objClass = inObj.getClass();
Field [] objFields =
	objClass.getFields();
int fieldsCount = objFields.length;
Next, we create the fieldsArray and an object array that will be used to create the GUI. More specifically, the array for the GUI will contain alternating labels (field names) and JFormattedText elements. This is why it is twice the size of fieldsArray:
Object [] array =
	new Object[fieldsCount * 2];
fieldsArray = new
	FieldWrapper[fieldsCount];
For simplicity, we will only handle objects with five or less public data fields:
try {
	if (fieldsCount > 5) {
// more fields than we can handle,
// revert to simple information dialog
		throw new Exception(
		"too many fields to process");
		}
Next, we get the name of the field, and the name of the type of the field, using the type to decide how to instantiate the JFormattedTextField element. This operation combines the JFormattedTextField usage techniques from the other data type specific dialogs we have created earlier:
String fieldName =
	objFields[i].getName();
String fieldType =
objFields[i].getType().getName();
if (fieldType.equals("int")
|| fieldType.equals("long")) {
	array[2 * i] = fieldName;
	array[2 * i+1] = new
		JFormattedTextField(
	NumberFormat.getIntegerInstance());
	fieldsArray[i] = new FieldWrapper(
	fieldName, (JFormattedTextField)
	array[2 * i + 1]);
	}
else if (fieldType.equals(
	"java.lang.String") &&
	fieldName.endsWith("P")) {
// this is a phone number field, a
// specialised string field - you can
// have many more here
	array[2 * i] = fieldName.substring(
	0,fieldName.length() -1);
	array[2 * i+1] = new
	JFormattedTextField(new
	MaskFormatter("(###) ###-####"));
	fieldsArray[i] = new
	FieldWrapper(fieldName,
	(JFormattedTextField)
	array[2 * i + 1]);
				}
else if (fieldType.equals(
	"java.lang.String")) {
// this is a regular string field
	array[2* i] = fieldName;
	array[2* i+1] = new
JFormattedTextField(new String(""));
	fieldsArray[i] = new FieldWrapper(
	fieldName, 	(JFormattedTextField)
	array[2 * i + 1]);
	}
else {
// at least one field we cannot
// process, reverting to simple
// information dialog
	throw new Exception(
	"field type not supported
	exception");
	} } // of for loop
	} catch (Exception ex) {
If the object has more than five fields, or if there is at least one field that we cannot handle, then we create a error status dialog box instead and print a stack trace:
ex.printStackTrace(System.err);
fieldsArray = null;
array = new Object[1];
array[0] = "Sorry, class cannot
be handled by ObjectInputDialog";
	}
createPane(array);
}
If the user clicks the OK button, we go through the fieldsArray and transfer the data from the GUI field into the object's data field:
protected void afterOKButtonClick() {
	if (fieldsArray != null)
	for (int i=0, n=fieldsArray.length;
		i<n; i++) {
fieldsArray[i].xferData(inputObject);
		} }
protected void
	afterCancelButtonClick() {
	// no need to do anything
	}
Before the dialog is hidden, we go through the fieldsArray and clear out all the JFormattedTextField elements via the zapData() method. This will ensure that the fields will all be null when the dialog is reused:
protected void beforeClosing() {
	// need to reset all the textfields
	if (fieldsArray != null)
	for (int i=0, n= fieldsArray.length;
		i<n; i++) {
		fieldsArray[i].zapData();
		} }
The last three methods are public accessors to the prompt text and the input object.
	public void
	setPromptMessage(String inText) {
		messageLabel.setText(inText);
		}
	public Object getInputObject() {
		return inputObject;
		}
	public void
		setInputObject(Object inObj) {
		inputObject = inObj;
		} }
Unlike other custom dialogs such as DateInputDialog, each object type that needs to input requires its own dialog instance. For example, we need one custom dialog to input the BankAccount class, and we need one custom dialog instance for the input of the BankAccount2 class. On the other hand, if we show the dialog for inputting a BankAccount class multiple times, then we don't want JustNuff to create a new dialog instance each time the showObjectInputModalDialog() method is called. The best way to accomplish this is to create a map that tracks the dialog instances (that are already created) and maps them to the corresponding object class that they handle. See the code fragment below:
private static final Map dialogStore =
new HashMap();
...
Object objectRetVal = null;
public Object
	showObjectInputModalDialog(String
	inText, Object inObj) throws
	Exception {
	final ObjectInputDialog curDialog;
	Class tpClass = inObj.getClass();
	if (dialogStore.containsKey(
		tpClass))
		curDialog = (ObjectInputDialog)
			dialogStore.get(tpClass);
	else {
		// create and add a new object
		// dialog for the class
		curDialog = new
			ObjectInputDialog(null,"",
			inObj);
		dialogStore.put(tpClass,
			curDialog);
		}
	...
The keys to the dialogStore HashMap are the classes of the object, while the values are the dialogs that are created to handle each corresponding class. This approach makes sure that we only create a single instance of a custom dialog for each class that we can handle.

Conclusion

It is somewhat ironic, yet predictable, that the creation of an easy-to-use "just enough" GUI library in Java requires an almost expert level of knowledge of the Swing GUI library. To alleviate the need for the user to handle tricky thread-safety problems, we handle it in the library. To ensure that GUI objects are allocated efficiently, we have programmed in object reuse via the static singleton coding pattern. To deploy the latest JDK 1.4 GUI elements, we've made extensive use of the new JFormattedTextField, including masked input. To provide ease-of-use when inputting multiple fields in an object, we've used the reflection API to dynamically discover the composition of the object during runtime. Last but not least, by re-factoring the relatively complex logic used to subclass JDialog when creating a custom dialog, we have enabled the JustNuff library to be extended easily to support additional data type specific or other custom dialogs. This behind-the-scenes look at the JustNuff GUI library offers plenty of food for thought for library builders and GUI developers alike.


Sing Li is a consultant, trainer and freelance writer specialising in Java, web applications, distributed computing and peer-to-peer technologies. His recent publications include Early Adopter JXTA, Professional JINI and Professional Apache Tomcat, all published by Wrox Press.

You might also like...

Comments

About the author

Sing Li United States

Sing Li has been writing software, and writing about software for twenty plus years. His specialities include scalable distributed computing systems and peer-to-peer technologies. He now spends ...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Every language has an optimization operator. In C++ that operator is ‘//’”