Java tutorial
/* * Copyright (c) 2008-2011 Simon Ritchie. * All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see http://www.gnu.org/licenses/>. */ package org.rimudb.editor; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.event.ActionListener; import java.util.ArrayList; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.DefaultCellEditor; import javax.swing.DefaultListModel; import javax.swing.DropMode; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.TransferHandler; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import org.apache.commons.logging.*; import org.rimudb.configuration.DescriptorModel; import org.rimudb.configuration.FieldEntry; import org.rimudb.editor.swing.ATable; import org.rimudb.editor.swing.ColumnPanel; import org.rimudb.editor.util.ValidationMessage; import org.rimudb.types.DatabaseTypes; /** * This class is responsible for displaying a tabbed view that permits editing * of a table descriptor. * * @author Simon Ritchie * */ public class DescriptorEditor extends JPanel implements ActionListener, DocumentListener { private final static Log log = LogFactory.getLog(DescriptorEditor.class); private static final long serialVersionUID = 1L; private ArrayList<String> illegalPropertiesList = new ArrayList<String>(); private RimuDBEditor rimuDBEditor = null; private JTextField packageTF = null; private JTextField dataObjectNameTF = null; private JTextField tableNameTF = null; private ATable table = null; private PropertyTableModel propertyModel = null; private TablePopupMenu propertyPopup = null; private JButton addColumnBtn = null; private JButton removeColumnBtn = null; private JButton moveUpBtn = null; private JButton moveDownBtn = null; private JButton addPkBtn; private JButton removePkBtn; private DefaultListModel pkListModel; private JList pkList; private boolean changed = false; /** * Constructor for DescriptorEditor. */ public DescriptorEditor(RimuDBEditor rimuDBEditor) { super(); this.rimuDBEditor = rimuDBEditor; setLayout(new BorderLayout()); setBorder(BorderFactory.createEmptyBorder(0, 7, 7, 7)); // Load the illegal properties list. These are property names that // are not permitted because they are already used by a DataObject // or a superclass. illegalPropertiesList.add("class"); illegalPropertiesList.add("changedproperties"); illegalPropertiesList.add("database"); illegalPropertiesList.add("lockresult"); illegalPropertiesList.add("originalrecord"); illegalPropertiesList.add("primarykeylist"); illegalPropertiesList.add("record"); illegalPropertiesList.add("table"); illegalPropertiesList.add("tablemetadata"); illegalPropertiesList.add("adding"); illegalPropertiesList.add("modified"); illegalPropertiesList.add("noargsconstructorused"); illegalPropertiesList.add("isrecordconstructorused"); illegalPropertiesList.add("isvirtual"); JPanel descriptorPanel = new JPanel(); descriptorPanel.setLayout(new BorderLayout()); descriptorPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5)); descriptorPanel.add(createDefinitionPanel(), BorderLayout.NORTH); descriptorPanel.add(createColumnTablePanel(), BorderLayout.CENTER); descriptorPanel.add(createButtonPanel(), BorderLayout.EAST); add(descriptorPanel, BorderLayout.CENTER); setPreferredSize(new Dimension(750, 500)); } /** * Clear the displayed data */ public void clearData() { // Clear all the models getPropertyModel().clear(); pkListModel.clear(); dataObjectNameTF.setText(""); tableNameTF.setText(""); clearChanged(); } private JPanel createDefinitionPanel() { packageTF = new JTextField(40); packageTF.setName("PackageTextField"); packageTF.getDocument().addDocumentListener(this); dataObjectNameTF = new JTextField(30); dataObjectNameTF.setName("DataObjectNameTextField"); dataObjectNameTF.getDocument().addDocumentListener(this); tableNameTF = new JTextField(30); tableNameTF.setName("TableNameTextField"); tableNameTF.getDocument().addDocumentListener(this); // Create the pane ColumnPanel defnPanel = new ColumnPanel(); defnPanel.setBorder(BorderFactory.createEmptyBorder(7, 0, 7, 0)); defnPanel.addWestJustify(new Component[] { new JLabel("Package:"), packageTF }); defnPanel.addWestJustify(new Component[] { new JLabel("Data Object name:"), dataObjectNameTF, new JLabel(" "), new JLabel("Table:"), tableNameTF }); return defnPanel; } private JPanel createButtonPanel() { addColumnBtn = createMenuButtonVert("Add..."); addColumnBtn.setName("AddColumnBtn"); addColumnBtn.addActionListener(this); removeColumnBtn = createMenuButtonVert("Remove"); removeColumnBtn.setName("RemoveColumnBtn"); removeColumnBtn.setEnabled(false); removeColumnBtn.addActionListener(this); moveUpBtn = createMenuButtonVert("Move up"); moveUpBtn.setName("MoveUpBtn"); moveUpBtn.setEnabled(false); moveUpBtn.addActionListener(this); moveDownBtn = createMenuButtonVert("Move down"); moveDownBtn.setName("MoveDownBtn"); moveDownBtn.setEnabled(false); moveDownBtn.addActionListener(this); addPkBtn = createMenuButtonVert("Add..."); addPkBtn.setName("AddPkBtn"); addPkBtn.addActionListener(this); removePkBtn = createMenuButtonVert("Remove"); removePkBtn.setName("RemovePkBtn"); removePkBtn.setEnabled(false); removePkBtn.addActionListener(this); JPanel buttonPanel = new JPanel(); buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 0)); buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.Y_AXIS)); buttonPanel.add(addColumnBtn); buttonPanel.add(Box.createRigidArea(new Dimension(0, 5))); buttonPanel.add(removeColumnBtn); buttonPanel.add(Box.createRigidArea(new Dimension(0, 5))); buttonPanel.add(moveUpBtn); buttonPanel.add(Box.createRigidArea(new Dimension(0, 5))); buttonPanel.add(moveDownBtn); buttonPanel.add(Box.createRigidArea(new Dimension(0, 242))); buttonPanel.add(addPkBtn); buttonPanel.add(Box.createRigidArea(new Dimension(0, 5))); buttonPanel.add(removePkBtn); buttonPanel.add(Box.createGlue()); return buttonPanel; } /** * Build the panel */ private JPanel createColumnTablePanel() { JPanel columnPanel = new JPanel(); columnPanel.setLayout(new BoxLayout(columnPanel, BoxLayout.Y_AXIS)); // Create the property table panel propertyModel = new PropertyTableModel(); // Add a listener to set the changed state propertyModel.addTableModelListener(new TableModelListener() { public void tableChanged(TableModelEvent e) { markChanged(); if (e instanceof PropertyTableModelEvent) { PropertyTableModelEvent ptme = (PropertyTableModelEvent) e; // If the columnName column was changed then check it isn't // a PK if (ptme.getColumn() == 1) { String beforeColumnName = (String) ptme.getBeforeValue(); String afterColumnName = (String) ptme.getAfterValue(); // Is the field entry in the list of primary keys? for (int i = 0; i < pkListModel.getSize(); i++) { String pkColumnName = (String) pkListModel.get(i); // If it's found then remove it if (beforeColumnName.equals(pkColumnName)) { pkListModel.set(i, afterColumnName); break; } } } } } }); table = new ATable(getPropertyModel()); table.setName("ColumnTable"); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { int selectedRowCount = table.getSelectedRowCount(); removeColumnBtn.setEnabled(selectedRowCount > 0); moveUpBtn.setEnabled(selectedRowCount > 0); moveDownBtn.setEnabled(selectedRowCount > 0); } }); table.setTransferHandler(new TransferHandler() { public int getSourceActions(JComponent c) { return COPY; } protected Transferable createTransferable(JComponent c) { ATable columnTable = (ATable) c; int row = columnTable.getSelectedRow(); String columnName = getPropertyModel().getRow(row).getColumnName(); return new StringSelection(columnName); } }); table.setDragEnabled(true); JScrollPane sp = new JScrollPane(table); sp.setMaximumSize(new Dimension(Short.MAX_VALUE, 325)); sp.setPreferredSize(new Dimension(Short.MAX_VALUE, 325)); sp.setMinimumSize(new Dimension(Short.MAX_VALUE, 325)); JComboBox typeCB = new JComboBox(DatabaseTypes.getAllTypes()); typeCB.setEditable(false); javax.swing.table.TableColumn typeColumn = table.getColumnModel().getColumn(2); typeColumn.setCellEditor(new DefaultCellEditor(typeCB)); // Create the popup menu and set it on the table propertyPopup = new TablePopupMenu(this, table); table.addMouseListener(propertyPopup); sp.addMouseListener(propertyPopup); sp.setAlignmentX(LEFT_ALIGNMENT); columnPanel.add(sp); columnPanel.add(Box.createVerticalStrut(10)); JLabel pkLabel = new JLabel("Primary Key Columns", SwingConstants.LEFT); pkLabel.setAlignmentX(LEFT_ALIGNMENT); columnPanel.add(pkLabel); pkListModel = new DefaultListModel(); pkListModel.addListDataListener(new ListDataListener() { public void intervalRemoved(ListDataEvent e) { markChanged(); } public void intervalAdded(ListDataEvent e) { markChanged(); } public void contentsChanged(ListDataEvent e) { markChanged(); } }); pkList = new JList(pkListModel); pkList.setName("pkList"); pkList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); pkList.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { int selectedRowCount = pkList.getSelectedIndex(); removePkBtn.setEnabled(selectedRowCount > -1); } }); pkList.setTransferHandler(new TransferHandler() { public boolean canImport(TransferHandler.TransferSupport info) { // we only import Strings if (!info.isDataFlavorSupported(DataFlavor.stringFlavor)) { return false; } JList.DropLocation dl = (JList.DropLocation) info.getDropLocation(); if (dl.getIndex() == -1) { return false; } return true; } public boolean importData(TransferHandler.TransferSupport info) { if (!info.isDrop()) { return false; } // Check for String flavor if (!info.isDataFlavorSupported(DataFlavor.stringFlavor)) { displayDropLocation("List doesn't accept a drop of this type."); return false; } JList.DropLocation dl = (JList.DropLocation) info.getDropLocation(); DefaultListModel listModel = (DefaultListModel) pkList.getModel(); int index = dl.getIndex(); // Get the string that is being dropped. Transferable t = info.getTransferable(); String data; try { data = (String) t.getTransferData(DataFlavor.stringFlavor); } catch (Exception e) { return false; } // If this is a copy action then check we don't already have that String if (info.getDropAction() == COPY && listModel.indexOf(data) > -1) { displayDropLocation("The column " + data + " is already a primary key"); return false; } // Perform the actual import. if (dl.isInsert()) { int oldIndex = listModel.indexOf(data); if (oldIndex < index) { listModel.add(index, data); listModel.remove(oldIndex); } else { listModel.remove(oldIndex); listModel.add(index, data); } } else { // Don't handle replacements } return true; } public int getSourceActions(JComponent c) { return MOVE; } protected Transferable createTransferable(JComponent c) { JList list = (JList) c; Object[] values = list.getSelectedValues(); StringBuffer buff = new StringBuffer(); for (int i = 0; i < values.length; i++) { Object val = values[i]; buff.append(val == null ? "" : val.toString()); if (i != values.length - 1) { buff.append("\n"); } } return new StringSelection(buff.toString()); } }); pkList.setDropMode(DropMode.INSERT); pkList.setDragEnabled(true); JScrollPane pkScrollPanel = new JScrollPane(pkList); pkScrollPanel.setMaximumSize(new Dimension(Short.MAX_VALUE, 100)); pkScrollPanel.setAlignmentX(LEFT_ALIGNMENT); columnPanel.add(pkScrollPanel); return columnPanel; } private void displayDropLocation(final String string) { SwingUtilities.invokeLater(new Runnable() { public void run() { JOptionPane.showMessageDialog(rimuDBEditor, string); } }); } /** * Invoked when an action occurs. */ public void actionPerformed(java.awt.event.ActionEvent evt) { // Property popup if (evt.getSource() == propertyPopup.getNewMenuItem() || evt.getSource() == addColumnBtn) { actionAddRow(); return; } if (evt.getSource() == propertyPopup.getDeleteMenuItem() || evt.getSource() == removeColumnBtn) { actionRemoveRow(); return; } if (evt.getSource() == propertyPopup.getMoveUpMenuItem() || evt.getSource() == moveUpBtn) { actionMoveUp(); return; } if (evt.getSource() == propertyPopup.getMoveDownMenuItem() || evt.getSource() == moveDownBtn) { actionMoveDown(); return; } if (evt.getSource() == addPkBtn) { actionAddPrimaryKey(); return; } if (evt.getSource() == removePkBtn) { actionRemovePrimaryKey(); return; } } /** * */ private void actionRemovePrimaryKey() { int index = pkList.getSelectedIndex(); pkListModel.remove(index); } /** * */ private void actionAddPrimaryKey() { log.debug("actionAddPrimaryKey()"); // Find unused primary key columns ArrayList<String> unusedColumnList = new ArrayList<String>(); String columnNames[] = propertyModel.getAllColumnNames(); for (int i = 0; i < columnNames.length; i++) { String column = columnNames[i]; // Check its not in the pkList boolean found = false; for (int j = 0; j < pkListModel.getSize(); j++) { if (column.equals(pkListModel.get(j))) { found = true; break; } } if (!found) { unusedColumnList.add(columnNames[i]); } } // Prompt the user for a column that is not currently a primary key AddPrimaryKeyDialog dialog = new AddPrimaryKeyDialog(rimuDBEditor, unusedColumnList); // if user chooses a column then add it to the list if (dialog.getResult() == AddPrimaryKeyDialog.RESULT_OK) { String column = dialog.getSelectedColumn(); pkListModel.addElement(column); } } /** * Create a button for a menu */ private JButton createMenuButtonVert(String text) { JButton btn = new JButton(text); btn.setAlignmentX(CENTER_ALIGNMENT); int height = btn.getMaximumSize().height; btn.setMaximumSize(new Dimension(Short.MAX_VALUE, height)); return btn; } /** * Add a row to the table. */ private void actionAddRow() { log.debug("actionAddRow()"); AddPropertyDialog dialog = new AddPropertyDialog(rimuDBEditor); dialog.setModal(true); dialog.setVisible(true); if (dialog.getUserAction() == AddPropertyDialog.ACTION_OK) { FieldEntry fieldEntry = new FieldEntry(); fieldEntry.setPrimaryKeyNbr(0); fieldEntry.setPropertyName(dialog.getPropertyName()); fieldEntry.setColumnName(dialog.getColumnName()); fieldEntry.setColumnType(dialog.getSQLType()); fieldEntry.setColumnSize(dialog.getColumnSize()); fieldEntry.setDecimalDigits(dialog.getDecimalDigits()); fieldEntry.setNullCapable(dialog.isNullable()); fieldEntry.setAutoIncrement(dialog.isIdentity()); fieldEntry.setRightJustify(dialog.isRightJustify()); fieldEntry.setVersion(dialog.isVersionColumn()); fieldEntry.setSequenceName(dialog.getSequence()); getPropertyModel().addEntry(fieldEntry); } } /** * Remove a row from the table. */ private void actionRemoveRow() { log.debug("actionRemoveRow()"); int row = table.getSelectedRow(); if (row >= 0) { FieldEntry fieldEntry = getPropertyModel().getRow(row); // Is the field entry in the list of primary keys? for (int i = 0; i < pkListModel.getSize(); i++) { String pkColumnName = (String) pkListModel.get(i); // If it's found then remove it if (fieldEntry.getColumnName().equals(pkColumnName)) { pkListModel.remove(i); break; } } // Remove the FieldEntry from the propertyModel getPropertyModel().removeRow(row); } } /** * Move the row down. */ private void actionMoveDown() { log.debug("actionMoveDown()"); int row = table.getSelectedRow(); if (row >= 0 && row < getPropertyModel().getRowCount() - 1) { getPropertyModel().moveRowDown(row); table.getSelectionModel().setSelectionInterval(row + 1, row + 1); } } /** * Move the row up. */ private void actionMoveUp() { log.debug("actionMoveUp()"); int row = table.getSelectedRow(); if (row >= 1) { getPropertyModel().moveRowUp(row); table.getSelectionModel().setSelectionInterval(row - 1, row - 1); } } /** * Set the DescriptorModel * * @param descriptorModel DescriptorModel */ public void setDescriptorModel(DescriptorModel descriptorModel) { log.debug("setDescriptorModel()"); packageTF.setText(descriptorModel.getPackageName()); dataObjectNameTF.setText(descriptorModel.getDataObjectName()); tableNameTF.setText(descriptorModel.getTableName()); // Load the column elements getPropertyModel().clear(); pkListModel.clear(); // Load all the FieldEntry instances into the property model. FieldEntry[] fieldEntries = descriptorModel.getFieldEntries(); for (int i = 0; i < fieldEntries.length; i++) { getPropertyModel().addEntry(fieldEntries[i]); } // Load the primary key columns into the primary key list model. FieldEntry[] pkFieldEntries = descriptorModel.getPrimaryKeyFieldEntries(); for (int i = 0; i < pkFieldEntries.length; i++) { pkListModel.add(i, pkFieldEntries[i].getColumnName()); } rimuDBEditor.updateTitle(); // Clear the changed flag clearChanged(); } /** * Validate the data entered. Returns an array of messages if there are * validation errors. Returns null if there are no errors. * * @return ValidationMessage[] */ public ValidationMessage[] validateData() { log.debug("validateData()"); ArrayList<ValidationMessage> list = new ArrayList<ValidationMessage>(); if (getDataObjectName().length() == 0) { list.add(new ValidationMessage("A data object name must be entered before classes can be created.")); } if (getPackageName().length() == 0) { list.add(new ValidationMessage("A package name must be entered before classes can be created.")); } if (getNumberOfKeyColumns() == 0) { list.add(new ValidationMessage("At least one column must be chosen as a key.")); } // Check fields for legal property names for (int x = 0; x < getPropertyModel().getRowCount(); x++) { FieldEntry fe = getPropertyModel().getRow(x); String propertyName = fe.getPropertyName().toLowerCase().trim(); if (illegalPropertiesList.contains(propertyName)) { list.add(new ValidationMessage("Property name '" + propertyName + "' is not permitted.")); } // If the property name contains the word item and it's not right // justified then perhaps it's wrong if (!fe.isRightJustify()) { String rjPhrases[] = rimuDBEditor.getPreferences().getRJPhrases(); for (int i = 0; i < rjPhrases.length; i++) { if (propertyName.toLowerCase().lastIndexOf(rjPhrases[i]) > -1) { list.add(new ValidationMessage("Property name '" + propertyName + "' might be an item number. If it is then it should be specified as right justified.", ValidationMessage.WARNING)); } } } } if (list.size() == 0) return null; else return (ValidationMessage[]) list.toArray(new ValidationMessage[list.size()]); } /** * Create a descriptorModel representing the edited values */ public DescriptorModel createDescriptorModel() { log.debug("createDescriptorModel()"); DescriptorModel descriptorModel = new DescriptorModel(); descriptorModel.setDataObjectName(dataObjectNameTF.getText().trim()); descriptorModel.setPackageName(packageTF.getText().trim()); descriptorModel.setFieldEntries(propertyModel.getAllRows()); descriptorModel.setTableName(tableNameTF.getText().trim()); // Set the primary key number to zero for all the Field Entries FieldEntry[] fieldEntries = descriptorModel.getFieldEntries(); for (int j = 0; j < fieldEntries.length; j++) { fieldEntries[j].setPrimaryKeyNbr(0); } // For each PK column, set the primary key number for (int i = 0; i < pkListModel.size(); i++) { String pkColumnName = (String) pkListModel.get(i); for (int j = 0; j < fieldEntries.length; j++) { if (pkColumnName.equals(fieldEntries[j].getColumnName())) { fieldEntries[j].setPrimaryKeyNbr(i + 1); } } } return descriptorModel; } public void setPropertyModel(PropertyTableModel propertyModel) { this.propertyModel = propertyModel; } public PropertyTableModel getPropertyModel() { return propertyModel; } /** * Return the Data Object name * * @return String */ public String getDataObjectName() { return dataObjectNameTF.getText().trim(); } /** * Set the data object name. * * @param dataObjectName * String */ public void setDataObjectName(String dataObjectName) { dataObjectNameTF.setText(dataObjectName); } /** * Return the package name. * * @return String */ public String getPackageName() { return packageTF.getText().trim(); } /** * Return the number of columns chosen as primary keys. * * @return int */ public int getNumberOfKeyColumns() { return pkListModel.size(); } /** * Return table name. * * @return String */ public String getTableName() { return tableNameTF.getText(); } /** * @param tableName */ public void setTableName(String tableName) { tableNameTF.setText(tableName); } public void addFieldEntry(FieldEntry fieldEntry) { getPropertyModel().addEntry(fieldEntry); } public void addPrimaryKey(String columnName) { pkListModel.addElement(columnName); } private void markChanged() { changed = true; rimuDBEditor.updateTitle(); } /** * Return true if some data in the editor has changed. * * @return boolean */ public boolean hasChanged() { return changed; } public void clearChanged() { changed = false; rimuDBEditor.updateTitle(); } /* * (non-Javadoc) * * @seejavax.swing.event.DocumentListener#changedUpdate(javax.swing.event. * DocumentEvent) */ public void changedUpdate(DocumentEvent e) { markChanged(); } /* * (non-Javadoc) * * @seejavax.swing.event.DocumentListener#insertUpdate(javax.swing.event. * DocumentEvent) */ public void insertUpdate(DocumentEvent e) { markChanged(); } /* * (non-Javadoc) * * @seejavax.swing.event.DocumentListener#removeUpdate(javax.swing.event. * DocumentEvent) */ public void removeUpdate(DocumentEvent e) { markChanged(); } /** * Set the package name. * * @param packageName * String */ public void setPackageName(String packageName) { packageTF.setText(packageName); } public void requestPackageFocus() { packageTF.requestFocus(); } }