Java Database Application Library


Get JDAL (Java Database Application Library) at SourceForge.net. Fast, secure and Free Open Source software downloads

Presentation Tier

We use jdal-swing to make the presentation layer of the library application. Before begining coding the presentation classes, we need to know how jdal-swing resolves the common problems of presentation programming. Those problems are:

This is done using (oh! surprise) Views, Validators and Binders. Jdal swing library tries to make this process as simple as possible, increasing the productivity by developing this layer.

Views

The interface View<T> contains the basic operations for model Views:

AbstractView is a Template (GoF) that support automatic binding the UI controls to model properties. The template defines four callbacks we only need to implement one of them. the buildPanel()

Lets go to implement the three Views: AuthorView, BookView and FilterView.

AuthorView

To create AuthorView we extend AbstractView <Author> and implement two methods:

AbstractView finds the appropiate binder (TexComponentBinder) using the configured BinderFactory. So usually, we´ll only need to call bind method with the control and the property name that we want to bind on the model.

public class AuthorView extends AbstractView<Author> {
	private JTextField name = new JTextField(25);
	private JTextField surname = new JTextField(25);
 
	public AuthorView() {
		this(new Author());
	}
 
	public AuthorView(Author author) {
		setModel(author);
		refresh();
	}
 
	public void init() {
		bind(name, "name");
		bind(surname, "surname");
	}
 
	@Override
	public JComponent buildPanel() {
		BoxFormBuilder fb = new BoxFormBuilder();
		fb.add("Name: ", name);
		fb.row();
		fb.add("Surname: ", surname);
		JComponent form = fb.getForm();
		form.setBorder(FormUtils.createTitledBorder("Author"));
 
		return form;
	}
}
 

follow the spring context configuration of AuthorView in applicationContext-view.xml

<!-- Abstract bean definition for Views -->
<bean id="view" abstract="true">
	<property name="binderFactory" ref="binderFactory" />
	<property name="messageSource" ref="messageSource" />
</bean>
 
<!--  AuthroView -->
<bean id="authorView" class="org.jdal.samples.library.ui.AuthorView"
	scope="prototype" parent="view" />
 
<!-- AuthorDialgo that save Author on AcceptAction -->
<bean id="authorDialog" class="info.joseluismartin.gui.ViewDialog" scope="prototype">
	<property name="view" ref="authorView" />
	<property name="acceptAction" ref="acceptAction" />
	<property name="cancelAction" ref="cancelAction" />
</bean>
 
<!-- Generic AcceptAction for dialogs, save models using <Object> persistentService -->
<bean id="acceptAction" class="info.joseluismartin.gui.action.ViewSaveAction"
	scope="prototype">
	<property name="icon" value="/images/16x16/dialog-ok.png" />
	<property name="service" ref="persistentService" />
	<property name="name" value="Accept" />
</bean>
 
<!-- Generic CancelAction for ViewDialogs, close dialog -->
<bean id="cancelAction" class="info.joseluismartin.gui.action.DialogCancelAction"
	scope="prototype">
	<property name="icon" value="/images/16x16/dialog-cancel.png" />
	<property name="name" value="Cancel" />
</bean>
 

BookView

For BookView we´ll follow the same strategy as for the author. Although this view is more complicated, the procedure is essentially the same.

In this case it is necessary to load in the JcomboBox the list of categories that we are going to use to select/display the category of a book. The ComboBoxModel ListComboBoxModel is used as a list container.

We also need the GuiFactory instance, a simple wrapper for the Spring ApplicationContext, to get instances of the bean "authorDialog" in order to add an author that is not already in the database.

Finally we add autocompletion to author combo using the hibernate filter that we showed earlier in the Author class. We create an instance of FilterAutoCompletionListener and set the author persistent service.

public class BookView extends AbstractView<Book> {
 
	private static final String ADD_ICON = "/images/16x16/list-add.png";
 
	private JTextField name = new JTextField();
	private JTextField isbn = new JTextField();
	private JComboBox author = FormUtils.newCombo(25);
	private JCalendarCombo published = FormUtils.newJCalendarCombo();
	private JComboBox category = FormUtils.newCombo(25);
	private String authorEditor = "authorEditor";
 
	private GuiFactory guiFactory;
	private PersistentService<Category, Long> categoryService;
	private AuthorService authorService;
 
	public BookView() {
		this(new Book());
	}
 
	public BookView(Book book) {
		setModel(book);
	}
 
	public void init() {
		autobind();  // bind controls to model by property name
	}
 
	@Override
	protected JComponent buildPanel() {
		// fill category combo with data from database.
		category.setModel(new ListComboBoxModel(categoryService.getAll()));
		// Add auto-completion to author combo, limit max results to 1000 and order data by surname
		FilterAutoCompletionListener acl = new FilterAutoCompletionListener(author, 1000, "surname");
		acl.setPersistentService(authorService);
		author.setEditable(true);
		// Create a Box with author combo and add button
		Box authorBox = Box.createHorizontalBox();
		authorBox.add(author);
		authorBox.add(Box.createHorizontalStrut(5));
		authorBox.add(new JButton(new AddAuthorAction(FormUtils.getIcon(ADD_ICON))));
		// Build Form with a BoxFormBuilder
		BoxFormBuilder fb = new BoxFormBuilder();
		fb.add("Title: ", name);
		fb.row();
		fb.add("Author: ", authorBox);	
		fb.row();
		fb.add("ISBN: ", isbn);
		fb.row();
		fb.add("Published Date:", published);
		fb.row();
		fb.add("Category", category);
 
		JComponent form = fb.getForm();
		form.setBorder(FormUtils.createTitledBorder("Book"));
		return form;
	}
 
 
 
	private class AddAuthorAction extends AbstractAction  {
 
		public AddAuthorAction(Icon icon) {
			putValue(Action.SMALL_ICON, icon);
		}
 
		public void actionPerformed(ActionEvent e) {
			ViewDialog dlg = (ViewDialog) guiFactory.getDialog(authorEditor);
			dlg.setModal(true);
			dlg.setVisible(true);
			if (dlg.getValue() == ViewDialog.OK) {
				getModel().setAuthor((Author) dlg.getModel());
				refresh();
			}
 
		}
 
	}
 
	// Getters and Setters ...
 
}
 

BookFilterView

Last view refers to BookFilter. Again. we´ll bind the controls and properties in init() method and create the JComponent in buildPanel() method

public class BookFilterView extends AbstractView<BookFilter> {
 
	private JTextField name = new JTextField(20);
	private JTextField authorName = new JTextField(20);
	private JTextField authorSurname = new JTextField(20);
	private JCalendarCombo before = FormUtils.newJCalendarCombo();
	private JCalendarCombo after = FormUtils.newJCalendarCombo();
	private JComboBox category = FormUtils.newCombo(20);
 
	private PersistentService<Category, Long> categoryService;
 
	public BookFilterView() {
		this(new BookFilter());
	}
 
	public BookFilterView(BookFilter filter) {
		setModel(filter);
	}
 
	public void init() {
		bind(name, "name");
		bind(authorName, "authorName");
		bind(authorSurname, "authorSurname");
		bind(before, "before");
		bind(after, "after");
		bind(category, "category");
	}
 
	@Override
	protected JComponent buildPanel() {
		BoxFormBuilder fb = new BoxFormBuilder();
 
		fb.add("Title: ", name);
		fb.add("Author Name: ", authorName);
		fb.add("Author Surname: ", authorSurname);
		fb.row();
		fb.add("Category: ", category);
		fb.add("Published Before: ", before);
		fb.add("Published After: ", after);
 
		JComponent box = fb.getForm();
		box.setBorder(FormUtils.createTitledBorder("Book Filter"));
 
		return box;
	}
 
	@Override
	public void doRefresh() {
		List<Category> categories = categoryService.getAll();
		categories.add(0, null);
		category.setModel(new ListComboBoxModel(categories));
 
	}
 
	// Getters and Setters...
 
}
 

This view finished the Java programming in the applicaton. We still need three tables, but we can get them from the library by configuration only.

Pageable Table

PageableTable it's a swing JPanel containig a JTable, a PaginatorView, a ListTableModel and a PageableDataSource.

The class ListTableModel is a TableModel that allows easy configuration from context configuration files. The method setComlumns(Columns columns) on ListTableModel allows to set column definitions as follows:

<bean id="bookTableModel" class="info.joseluismartin.gui.ListTableModel" scope="prototype">
        <property name="modelClass" value="org.jdal.samples.library.model.Book"/>
        <property name="columns">
            <list value-type="info.joseluismartin.gui.ColumnDefinition">
                <bean class="info.joseluismartin.gui.ColumnDefinition">
                    <property name="name" value="name"/>
                    <property name="displayName" value="Name"/>
                </bean>
                <bean class="info.joseluismartin.gui.ColumnDefinition">
                    <property name="name" value="author"/>
                    <property name="displayName" value="Author"/>
                    <property name="sortProperty" value="author.name"/>
                </bean>
                    <bean class="info.joseluismartin.gui.ColumnDefinition">
                    <property name="name" value="category"/>
                    <property name="displayName" value="Category"/>
                    <property name="sortProperty" value="category.name"/>
                </bean>
                    <bean class="info.joseluismartin.gui.ColumnDefinition">
                    <property name="name" value="isbn"/>
                    <property name="displayName" value="ISBN"/>
                </bean>
            </list>
        </property>
        <property name="usingActions" value="true"/>
        <property name="usingChecks" value="true"/>
    </bean>

In the inner bean Column we can set up this properties:

By using the actions property we can set the RowActions. On the other hand,the usingChecks property let us show a column of checkboxes in order to select rows in the table.

Table Panel

TablePanel is a JPanel with a PageabeTable, a button panel and a filterPanel as show in following figure:


The button panel is filled with TablePanelActions that are simply Actions with a TablePanel reference. Writing Actions to show in the TablePanel is easy, you only have to extend TablePanelAction, implement the actionPerformed method, and add the Action to ActionList in TablePanel bean definition. The library provides defaults action for adding, deleteting, finding, hiding/showing filters, as shown before.

The following box show the configuration context details of the sample book TablePanel.

<!--  TablePanel abstract configuration -->"
<bean name="tablePanel" abstract="true">
    <!-- Default Actions for TablePanel -->
	<property name="actions">
		<list value-type="java.awt.Action">
			<bean class="info.joseluismartin.gui.table.AddAction" />
			<bean class="info.joseluismartin.gui.table.SelectAllAction" />
			<bean class="info.joseluismartin.gui.table.DeselectAllAction" />
			<bean class="info.joseluismartin.gui.table.RemoveAllAction" />
			<bean class="info.joseluismartin.gui.table.HideShowFilterAction" />
			<bean class="info.joseluismartin.gui.table.ApplyFilterAction" />
			<bean class="info.joseluismartin.gui.table.ClearFilterAction" />
		</list>
	</property>
</bean>
 
<!-- Paginator for PageableTable -->
<bean id="paginatorView" class="info.joseluismartin.gui.PaginatorView"
	scope="prototype">
	<property name="firstIcon" value="images/table/22x22/go-first.png" />
	<property name="lastIcon" value="images/table/22x22/go-last.png" />
	<property name="nextIcon" value="images/table/22x22/go-next.png" />
	<property name="previousIcon" value="images/table/22x22/go-previous.png" />
	<property name="pageSizes" value="10,20,30,40,50,100,All" />
</bean>
 
<!-- Pageable Table of Books -->
<bean id="bookTable" class="info.joseluismartin.gui.PageableTable">
	<property name="dataSource" ref="bookService" />
	<property name="tableModel" ref="bookTableModel" />
	<property name="paginatorView" ref="paginatorView" />
</bean>
 
<!-- Book TablePanel -->
<bean id="bookTablePanel" class="info.joseluismartin.gui.table.TablePanel"
	parent="tablePanel">
	<property name="table" ref="bookTable" />
	<property name="filterView" ref="filterView" />
	<!--  bean name of model editor, show it on double-clicks on rows -->
	<property name="editorName" value="bookDialog" />
	<property name="guiFactory" ref="guiFactory" />
	<property name="persistentService" ref="bookService" />
</bean>
 
<bean id="filterView" class="org.jdal.samples.library.ui.BookFilterView"
	parent="view">
	<property name="categoryService" ref="categoryService" />
</bean>

List Panel

Finally, we set up a ListPane (a JComponent container using a JList similar to JTabbedPane) to hold the book table panel and the autor and category table editors. ListPane accept only PanelHolders as components, so we need to wrap the Views and JComponents with an inner bean definition of appropiate PanelHolder:

<bean id="listPanel" class="info.joseluismartin.gui.ListPane">
	<property name="panels">
		<list value-type="info.joseluismartin.gui.PanelHolder">
			<bean class="info.joseluismartin.gui.JComponentPanelHolder">
				<property name="name" value="Books" />
				<property name="component" ref="bookTablePanel" />
			</bean>
			<bean class="info.joseluismartin.gui.ViewPanelHolder">
				<property name="view" ref="authorEditor" />
			</bean>
			<bean class="info.joseluismartin.gui.ViewPanelHolder">
				<property name="view" ref="categoryEditor" />
			</bean>
		</list>
	</property>
</bean>