/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jdal.swing;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdal.beans.MessageSourceWrapper;
import org.jdal.dao.Dao;
import org.jdal.swing.action.BeanAction;
import org.jdal.swing.form.FormUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.dao.DataAccessException;
/**
* Simple table editor
*
* @author Jose Luis Martin - (jlm@joseluismartin.info)
*/
public class TableEditor<T> extends AbstractView<T> implements TableModelListener {
// Default Icons
// FIXME: Move to Tango Desktop Icons
public static String DEFAULT_ICON = "/images/table/table.png";
public static String DEFAULT_ADD_ICON = "/images/table/22x22/edit-new.png";
public static String DEFAULT_REMOVE_ICON = "/images/table/22x22/edit-delete.png";
public static String DEFAULT_SAVE_ICON = "/images/table/22x22/save.png";
public static String DEFAULT_REFRESH_ICON = "/images/table/22x22/reload.png";
/** log */
private static final Log log = LogFactory.getLog(TableEditor.class);
/** JTable to show database rows */
private JTable table;
/** Use ListTableModel as table TableModel */
private ListTableModel tableModel;
private Icon icon;
private Icon addIcon;
private Icon removeIcon;
private Icon saveIcon;
private Icon refreshIcon;
/** Hold dirty rows */
private Set<T> dirtyModels = new HashSet<T>();
/** Entity type */
private Class<T> clazz;
private String name;
/** Persistent Service to use */
private Dao<T, Serializable> service;
/** MessageSource */
private MessageSourceWrapper messageSource = new MessageSourceWrapper();
/**
* Creates new TableEditor
*/
public TableEditor() {
}
/**
* Creates new TableEditor
* @param clazz entity class
*/
public TableEditor(Class<T> clazz) {
this.clazz = clazz;
}
/**
* Initiaze, usally called by container
*/
public void init() {
loadIcons();
if (tableModel.getModelClass() == null)
tableModel.setModelClass(this.clazz);
refresh();
}
/**
* {@inheritDoc}
*/
@Override
protected JComponent buildPanel() {
Box box = Box.createVerticalBox();
Container tablePanel = createTablePanel();
box.add(tablePanel);
box.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
return box;
}
/**
* Creates a new Box with table and actions buttons
* @return a new Box
*/
protected Container createTablePanel() {
table = new JTable(tableModel, tableModel.getTableColumnModel());
table.setRowHeight(22);
table.setAutoCreateRowSorter(true);
tableModel.addTableModelListener(this);
JScrollPane scroll = new JScrollPane(table);
scroll.setAlignmentX(Container.LEFT_ALIGNMENT);
Box box = Box.createVerticalBox();
JButton addButton = new JButton(new AddAction());
JButton deleteButton = new JButton(new DeleteAction());
JButton saveButton = new JButton(new SaveAction());
JButton refreshButton = new JButton(new RefreshAction());
Box buttonBox = Box.createHorizontalBox();
buttonBox.add(addButton);
buttonBox.add(Box.createHorizontalStrut(5));
buttonBox.add(deleteButton);
buttonBox.add(Box.createHorizontalStrut(5));
buttonBox.add(saveButton);
buttonBox.add(Box.createHorizontalStrut(5));
buttonBox.add(refreshButton);
buttonBox.setAlignmentX(Container.LEFT_ALIGNMENT);
buttonBox.setMaximumSize(new Dimension(Short.MAX_VALUE, 25));
box.add(buttonBox);
box.add(Box.createVerticalStrut(5));
box.add(scroll);
return box;
}
/**
* {@inheritDoc}
*/
public void doRefresh() {
tableModel.setList(service.getAll());
}
/**
* @return header
*/
protected Container createHeader() {
JLabel label = new JLabel(getName());
label.setAlignmentX(Container.LEFT_ALIGNMENT);
Box box = Box.createHorizontalBox();
box.add(label);
return box;
}
/**
* Add a new model to the table
*/
public void add() {
if (onAdd()) {
T t = newType();
tableModel.add(t);
dirtyModels.add(t);
}
}
public void save() {
if (onSave()) {
try {
this.service.save(dirtyModels);
} catch (DataAccessException dae) {
if (onSaveError(dae)) {
String errorMsg = messageSource.getMessage("TableEditor.saveError", null, Locale.getDefault());
JOptionPane.showMessageDialog(getPanel(), errorMsg, "Error", JOptionPane.ERROR_MESSAGE);
}
}
finally {
dirtyModels.clear();
}
refresh();
}
}
/**
* Delete selected table rows using persistent service
*/
@SuppressWarnings("unchecked")
public void delete() {
if (onDelete()) {
int[] rows = table.getSelectedRows();
for (int i : rows) {
T model = (T) tableModel.getList().get(table.convertRowIndexToModel(i));
try {
service.delete(model);
tableModel.getList().remove(model);
} catch (DataAccessException dae) {
String errorMsg = messageSource.getMessage("TableEditor.objectInUse",
new Object[] {model.toString()}, Locale.getDefault());
JOptionPane.showMessageDialog(getPanel(), errorMsg, "Error", JOptionPane.ERROR_MESSAGE);
}
}
tableModel.fireTableChanged();
}
}
/**
* Create a new instance of configured entity class
* @return new entity
*/
protected T newType() {
try {
return clazz.newInstance();
} catch (Exception e) {
log.error("Can't instantiate class: ", e);
}
return null;
}
public JTable getTable() {
return table;
}
public void setTable(JTable table) {
this.table = table;
}
public ListTableModel getTableModel() {
return tableModel;
}
public void setTableModel(ListTableModel tableModel) {
this.tableModel = tableModel;
}
public Icon getAddIcon() {
return addIcon;
}
public void setAddIcon(Icon addIcon) {
this.addIcon = addIcon;
}
public Dao<T, Serializable> getService() {
return service;
}
public void setService(Dao<T, Serializable> service) {
this.service = service;
}
private class SaveAction extends IconAction {
private static final long serialVersionUID = 1L;
public SaveAction() {
setIcon(saveIcon);
}
public void actionPerformed(ActionEvent e) {
save();
}
}
private class AddAction extends IconAction {
private static final long serialVersionUID = 1L;
public AddAction() {
setIcon(addIcon);
}
public void actionPerformed(ActionEvent e) {
add();
}
}
private class DeleteAction extends IconAction {
private static final long serialVersionUID = 1L;
public DeleteAction() {
setIcon(removeIcon);
}
public void actionPerformed(ActionEvent e) {
delete();
}
}
private class RefreshAction extends BeanAction {
private static final long serialVersionUID = 1L;
public RefreshAction() {
setIcon(refreshIcon);
}
public void actionPerformed(ActionEvent e) {
dirtyModels.clear();
refresh();
}
}
public Icon getSaveIcon() {
return saveIcon;
}
public void setSaveIcon(Icon saveIcon) {
this.saveIcon = saveIcon;
}
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.UPDATE) {
int row = e.getFirstRow();
if (row >= 0) {
setDirtyRow(row);
}
}
}
/**
* @param row
*/
@SuppressWarnings("unchecked")
public void setDirtyRow(int row) {
dirtyModels.add((T) tableModel.getList().get(row));
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return getName();
}
/**
* @return the icon
*/
public Icon getIcon() {
return icon;
}
/**
* @param icon the icon to set
*/
public void setIcon(Icon icon) {
this.icon = icon;
}
protected void loadIcons() {
icon = FormUtils.getIcon(icon, DEFAULT_ICON);
addIcon = FormUtils.getIcon(addIcon, DEFAULT_ADD_ICON);
saveIcon = FormUtils.getIcon(saveIcon, DEFAULT_SAVE_ICON);
removeIcon = FormUtils.getIcon(removeIcon, DEFAULT_REMOVE_ICON);
refreshIcon = FormUtils.getIcon(refreshIcon, DEFAULT_REFRESH_ICON);
}
/**
* @return the removeIcon
*/
public Icon getRemoveIcon() {
return removeIcon;
}
/**
* @param removeIcon the removeIcon to set
*/
public void setRemoveIcon(Icon removeIcon) {
this.removeIcon = removeIcon;
}
/**
* @return the refreshIcon
*/
public Icon getRefreshIcon() {
return refreshIcon;
}
/**
* @param refreshIcon the refreshIcon to set
*/
public void setRefreshIcon(Icon refreshIcon) {
this.refreshIcon = refreshIcon;
}
/**
* @return the messageSource
*/
public MessageSource getMessageSource() {
return messageSource.getMessageSource();
}
/**
* @param messageSource the messageSource to set
*/
@Autowired
public void setMessageSource(MessageSource messageSource) {
this.messageSource.setMessageSource(messageSource);
}
/**
* Do something on add, return false to cancel
* @return boolean
*/
protected boolean onAdd() {
return true;
}
/**
* Do something on delete, return false to cancel
* @return boolean
*/
protected boolean onDelete() {
return true;
}
/**
* Do something on save, return false to cancel.
* @return true to save, false to cancel.
*/
protected boolean onSave() {
return true;
}
/**
* @return the dirtyModels
*/
protected Set<T> getDirtyModels() {
return dirtyModels;
}
/**
* Do something on save error
* @param dae exception
* @return true to run default handler, false otherwise.
*/
private boolean onSaveError(DataAccessException dae) {
return true;
}
}