/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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.
*
* Copyright (c) 2001 - 2009 Object Refinery Ltd, Pentaho Corporation and Contributors. All rights reserved.
*/
package org.pentaho.reporting.tools.configeditor;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.ScrollPaneConstants;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.libraries.base.boot.AbstractBoot;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.util.FilesystemFilter;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.base.util.ResourceBundleSupport;
import org.pentaho.reporting.libraries.xmlns.common.ParserUtil;
import org.pentaho.reporting.tools.configeditor.model.ClassConfigDescriptionEntry;
import org.pentaho.reporting.tools.configeditor.model.ConfigDescriptionEntry;
import org.pentaho.reporting.tools.configeditor.model.ConfigDescriptionModel;
import org.pentaho.reporting.tools.configeditor.model.EnumConfigDescriptionEntry;
import org.pentaho.reporting.tools.configeditor.model.TextConfigDescriptionEntry;
/**
* The config description editor is used to edit the configuration metadata used in the ConfigEditor to describe the
* ReportConfiguration keys.
*
* @author Thomas Morgner
*/
public class ConfigDescriptionEditor extends JFrame
{
private static final Log logger = LogFactory.getLog(ConfigDescriptionEditor.class);
private class CloseHandler extends WindowAdapter
{
private CloseHandler()
{
}
/**
* Invoked when a window is in the process of being closed. The close operation can be overridden at this point.
*/
public void windowClosing(final WindowEvent e)
{
attempExit();
}
}
/**
* A configuration key to define the Font used in the editor.
*/
protected static final String EDITOR_FONT_KEY = "org.pentaho.reporting.engine.classic.core.modules.gui.config.EditorFont"; //$NON-NLS-1$
/**
* A configuration key to define the Font size used in the editor.
*/
protected static final String EDITOR_FONT_SIZE_KEY = "org.pentaho.reporting.engine.classic.core.modules.gui.config.EditorFontSize"; //$NON-NLS-1$
/**
* An internal constant to activate the class detail editor.
*/
private static final String CLASS_DETAIL_EDITOR_NAME = "Class"; //$NON-NLS-1$
/**
* An internal constant to activate the enumeration detail editor.
*/
private static final String ENUM_DETAIL_EDITOR_NAME = "Enum"; //$NON-NLS-1$
/**
* An internal constant to activate the text detail editor.
*/
private static final String TEXT_DETAIL_EDITOR_NAME = "Text"; //$NON-NLS-1$
/**
* Handles close requests in this editor.
*/
private class CloseAction extends AbstractAction
{
/**
* Defaultconstructor.
*/
private CloseAction()
{
putValue(Action.NAME, getResources().getString("action.exit.name")); //$NON-NLS-1$
}
/**
* Handles the close request.
*
* @param e not used.
*/
public void actionPerformed(final ActionEvent e)
{
attempExit();
}
}
/**
* Handles save requests in this editor.
*/
private class SaveAction extends AbstractAction
{
/**
* Defaultconstructor.
*/
private SaveAction()
{
putValue(Action.NAME, getResources().getString("action.save.name")); //$NON-NLS-1$
putValue(Action.SMALL_ICON, getResources().getIcon("action.save.small-icon")); //$NON-NLS-1$
}
/**
* Handles the save request.
*
* @param e not used.
*/
public void actionPerformed(final ActionEvent e)
{
save();
}
}
/**
* Handles import requests in this editor. Imports try to build a new description model from a given report
* configuration.
*/
private class ImportAction extends AbstractAction
{
/**
* Defaultconstructor.
*/
private ImportAction()
{
putValue(Action.NAME, getResources().getString("action.import.name")); //$NON-NLS-1$
putValue(Action.SMALL_ICON, getResources().getIcon("action.import.small-icon")); //$NON-NLS-1$
setEnabled(configurationToEdit != null);
}
/**
* Handles the import request.
*
* @param e not used.
*/
public void actionPerformed(final ActionEvent e)
{
if (configurationToEdit == null)
{
return;
}
final ConfigDescriptionModel model = getModel();
model.importFromConfig(configurationToEdit);
model.sort();
setStatusText(getResources().getString("config-description-editor.import-complete")); //$NON-NLS-1$
}
}
/**
* Handles requests to add a new entry in this editor.
*/
private class AddEntryAction extends AbstractAction
{
/**
* Defaultconstructor.
*/
private AddEntryAction()
{
putValue(Action.NAME, getResources().getString("action.add-entry.name")); //$NON-NLS-1$
putValue(Action.SMALL_ICON, getResources().getIcon("action.add-entry.small-icon")); //$NON-NLS-1$
}
/**
* Handles the add-entry request.
*
* @param e not used.
*/
public void actionPerformed(final ActionEvent e)
{
final TextConfigDescriptionEntry te =
new TextConfigDescriptionEntry
(getResources().getString("config-description-editor.unnamed-entry")); //$NON-NLS-1$
final ConfigDescriptionModel model = getModel();
model.add(te);
getEntryList().setSelectedIndex(model.getSize() - 1);
}
}
/**
* Handles requests to remove an entry from this editor.
*/
private class RemoveEntryAction extends AbstractAction
{
/**
* Defaultconstructor.
*/
private RemoveEntryAction()
{
putValue(Action.NAME, getResources().getString("action.remove-entry.name")); //$NON-NLS-1$
putValue(Action.SMALL_ICON, getResources().getIcon("action.remove-entry.small-icon")); //$NON-NLS-1$
}
/**
* Handles the remove entry request.
*
* @param e not used.
*/
public void actionPerformed(final ActionEvent e)
{
final int[] selectedEntries = getEntryList().getSelectedIndices();
setSelectedEntry(null);
final ConfigDescriptionModel model = getModel();
model.removeAll(selectedEntries);
getEntryList().clearSelection();
}
}
/**
* Handles load requests in this editor.
*/
private class LoadAction extends AbstractAction
{
/**
* Defaultconstructor.
*/
private LoadAction()
{
putValue(Action.NAME, getResources().getString("action.load.name")); //$NON-NLS-1$
putValue(Action.SMALL_ICON, getResources().getIcon("action.load.small-icon")); //$NON-NLS-1$
}
/**
* Handles the laod request.
*
* @param e not used.
*/
public void actionPerformed(final ActionEvent e)
{
load();
}
}
/**
* Handles update requests in the detail editor.
*/
private class UpdateAction extends AbstractAction
{
/**
* Defaultconstructor.
*/
private UpdateAction()
{
putValue(Action.NAME, getResources().getString("action.update.name")); //$NON-NLS-1$
}
/**
* Handles the update request.
*
* @param e not used.
*/
public void actionPerformed(final ActionEvent e)
{
writeSelectedEntry();
}
}
/**
* Handles cancel requests in the detail editor.
*/
private class CancelAction extends AbstractAction
{
/**
* Defaultconstructor.
*/
private CancelAction()
{
putValue(Action.NAME, getResources().getString("action.cancel.name")); //$NON-NLS-1$
}
/**
* Handles the cancel request.
*
* @param e not used.
*/
public void actionPerformed(final ActionEvent e)
{
final ConfigDescriptionEntry ce = getSelectedEntry();
setSelectedEntry(null);
setSelectedEntry(ce);
}
}
/**
* Handles editor type selections within the detail editor.
*/
private class SelectTypeAction extends AbstractAction
{
/**
* the selected type.
*/
private final int type;
/**
* Creates a new select type action for the given name and type.
*
* @param name the name of the action.
* @param type the type that should be selected whenever this action gets called.
*/
private SelectTypeAction(final String name, final int type)
{
putValue(Action.NAME, name);
this.type = type;
}
/**
* Handles the select type request.
*
* @param e not used.
*/
public void actionPerformed(final ActionEvent e)
{
setEntryType(type);
}
}
/**
* Handles the list selection in the list of available config keys.
*/
private class ConfigListSelectionListener implements ListSelectionListener
{
private boolean inUpdate;
/**
* Defaultconstructor.
*/
private ConfigListSelectionListener()
{
}
/**
* Called whenever the value of the selection changes.
*
* @param e the event that characterizes the change.
*/
public void valueChanged(final ListSelectionEvent e)
{
if (inUpdate)
{
return;
}
inUpdate = true;
try
{
final ConfigDescriptionEntry newEntry;
final int selectedIndex = getEntryList().getSelectedIndex();
if (selectedIndex == -1)
{
newEntry = null;
}
else
{
newEntry = getModel().get(selectedIndex);
}
final ConfigDescriptionEntry oldEntry = getSelectedEntry();
if (oldEntry != null)
{
writeSelectedEntry();
}
if (newEntry != null)
{
final int index = getModel().indexOf(newEntry);
if (getEntryList().getSelectedIndex() != index)
{
getEntryList().setSelectedIndex(index);
}
setSelectedEntry(newEntry);
}
else
{
getEntryList().setSelectedIndex(-1);
setSelectedEntry(null);
}
}
finally
{
inUpdate = false;
}
}
}
/**
* Handles list selections in the enumeration detail editor.
*/
private class EnumerationListSelectionHandler implements ListSelectionListener
{
/**
* Defaultconstructor.
*/
private EnumerationListSelectionHandler()
{
}
/**
* Called whenever the value of the selection changes.
*
* @param e the event that characterizes the change.
*/
public void valueChanged(final ListSelectionEvent e)
{
if (getEnumEntryList().getSelectedIndex() == -1)
{
getEnumEntryEditField().setText(""); //$NON-NLS-1$
}
else
{
getEnumEntryEditField().setText((String) getEnumEntryListModel().get
(getEnumEntryList().getSelectedIndex()));
}
}
}
/**
* A ShortCut action to redefine the entries of the enumeration detail editor to represent a boolean value.
*/
private class SetBooleanEnumEntryAction extends AbstractAction
{
/**
* Defaultconstructor.
*/
private SetBooleanEnumEntryAction()
{
putValue(Action.NAME, getResources().getString("action.boolean.name")); //$NON-NLS-1$
}
/**
* Handles the boolean redefinition request.
*
* @param e not used.
*/
public void actionPerformed(final ActionEvent e)
{
final DefaultListModel enumEntryListModel = getEnumEntryListModel();
enumEntryListModel.clear();
getEnumEntryEditField().setText(""); //$NON-NLS-1$
enumEntryListModel.addElement("true"); //$NON-NLS-1$
enumEntryListModel.addElement("false"); //$NON-NLS-1$
}
}
/**
* Handles the request to add a new enumeration entry to the detail editor.
*/
private class AddEnumEntryAction extends AbstractAction
{
/**
* Defaultconstructor.
*/
private AddEnumEntryAction()
{
putValue(Action.NAME, getResources().getString("action.add-enum-entry.name")); //$NON-NLS-1$
}
/**
* Handles the request to add a new enumeration entry to the detail editor.
*
* @param e not used.
*/
public void actionPerformed(final ActionEvent e)
{
getEnumEntryListModel().addElement(getEnumEntryEditField().getText());
}
}
/**
* Handles the request to remove an enumeration entry to the detail editor.
*/
private class RemoveEnumEntryAction extends AbstractAction
{
/**
* Defaultconstructor.
*/
private RemoveEnumEntryAction()
{
putValue(Action.NAME, getResources().getString("action.remove-enum-entry.name")); //$NON-NLS-1$
}
/**
* Handles the request to remove an enumeration entry to the detail editor.
*
* @param e not used.
*/
public void actionPerformed(final ActionEvent e)
{
final JList enumEntryList = getEnumEntryList();
final DefaultListModel enumEntryListModel = getEnumEntryListModel();
final int[] selectedEntries = enumEntryList.getSelectedIndices();
for (int i = selectedEntries.length - 1; i >= 0; i--)
{
enumEntryListModel.remove(selectedEntries[i]);
}
enumEntryList.clearSelection();
}
}
/**
* Handles the request to update an enumeration entry to the detail editor.
*/
private class UpdateEnumEntryAction extends AbstractAction
{
/**
* Defaultconstructor.
*/
private UpdateEnumEntryAction()
{
putValue(Action.NAME, getResources().getString("action.update-enum-entry.name")); //$NON-NLS-1$
}
/**
* Handles the request to update an enumeration entry to the detail editor.
*
* @param e not used.
*/
public void actionPerformed(final ActionEvent e)
{
final int idx = getEnumEntryList().getSelectedIndex();
if (idx == -1)
{
getEnumEntryListModel().addElement(getEnumEntryEditField().getText());
}
else
{
getEnumEntryListModel().setElementAt(getEnumEntryEditField().getText(), idx);
}
}
}
/**
* An internal value to mark a text detail editor type.
*/
private static final int TYPE_TEXT = 0;
/**
* An internal value to mark a class detail editor type.
*/
private static final int TYPE_CLASS = 1;
/**
* An internal value to mark a enumeration detail editor type.
*/
private static final int TYPE_ENUM = 2;
/**
* A radio button to select the text editor type for the current key.
*/
private JRadioButton rbText;
/**
* A radio button to select the class editor type for the current key.
*/
private JRadioButton rbClass;
/**
* A radio button to select the enumeration editor type for the current key.
*/
private JRadioButton rbEnum;
/**
* The list model used to collect and manage all available keys.
*/
private ConfigDescriptionModel model;
/**
* The name of the currently edited key.
*/
private JTextField keyNameField;
/**
* The description field contains a short description of the current key.
*/
private JTextArea descriptionField;
/**
* Allows to check, whether the key is a global (boot-time) key.
*/
private JCheckBox globalField;
/**
* Allows to check, whether the key is hidden.
*/
private JCheckBox hiddenField;
/**
* The name of the base class for the class detail editor.
*/
private JTextField baseClassField;
/**
* contains the currently selected entry of the enumeration detail editor.
*/
private JTextField enumEntryEditField;
/**
* contains all entries of the enumeration detail editor.
*/
private DefaultListModel enumEntryListModel;
/**
* The current resource bundle used to translate the strings in this dialog.
*/
private ResourceBundleSupport resources;
/**
* This cardlayout is used to display the currently selected detail editor.
*/
private CardLayout detailManager;
/**
* Contains the detail editor manager.
*/
private JPanel detailManagerPanel;
/**
* Contains the detail editor for the key.
*/
private JPanel detailEditorPane;
/**
* The list is used to manage all available keys.
*/
private JList entryList;
/**
* This list is used to manage the available entries of the enumeration detail editor.
*/
private JList enumEntryList;
/**
* the currently selected description entry.
*/
private ConfigDescriptionEntry selectedEntry;
/**
* The file chooser is used to select the file for the load/save operations.
*/
private JFileChooser fileChooser;
/**
* Serves as statusline for the dialog.
*/
private JLabel statusHolder;
/**
* The currently selected detail editor type.
*/
private int type;
private Configuration configurationToEdit;
public ConfigDescriptionEditor()
{
this(ConfigEditorBoot.class.getName());
}
/**
* Constructs a ConfigDescriptionEditor that is initially invisible.
*/
public ConfigDescriptionEditor(final AbstractBoot boot)
{
if (boot != null)
{
boot.start();
this.configurationToEdit = boot.getGlobalConfig();
}
init();
}
/**
* Constructs a ConfigDescriptionEditor that is initially invisible.
*/
public ConfigDescriptionEditor(final String booterClass)
{
final AbstractBoot boot = (AbstractBoot)
ObjectUtilities.loadAndInstantiate(booterClass, ConfigDescriptionEditor.class, AbstractBoot.class);
if (boot != null)
{
boot.start();
this.configurationToEdit = boot.getGlobalConfig();
}
init();
}
private void init()
{
this.resources = Messages.getInstance();
setTitle(resources.getString("config-description-editor.title")); //$NON-NLS-1$
final JPanel contentPane = new JPanel();
contentPane.setLayout(new BorderLayout());
detailEditorPane = createEditPane();
final JSplitPane splitPane = new JSplitPane
(JSplitPane.HORIZONTAL_SPLIT, createEntryList(), detailEditorPane);
contentPane.add(splitPane, BorderLayout.CENTER);
contentPane.add(createButtonPane(), BorderLayout.SOUTH);
final JPanel cPaneStatus = new JPanel();
cPaneStatus.setLayout(new BorderLayout());
cPaneStatus.add(contentPane, BorderLayout.CENTER);
cPaneStatus.add(createStatusBar(), BorderLayout.SOUTH);
setContentPane(cPaneStatus);
setEntryType(ConfigDescriptionEditor.TYPE_TEXT);
setSelectedEntry(null);
fileChooser = new JFileChooser();
fileChooser.addChoosableFileFilter(new FilesystemFilter
(".xml", resources.getString("config-description-editor.xml-files"))); //$NON-NLS-1$ //$NON-NLS-2$
fileChooser.setMultiSelectionEnabled(false);
setStatusText(resources.getString("config-description-editor.welcome")); //$NON-NLS-1$
addWindowListener(new CloseHandler());
}
/**
* Creates and returns the entry list component that will hold all config description entries within a list.
*
* @return the created entry list.
*/
private JPanel createEntryList()
{
final Action addEntryAction = new AddEntryAction();
final Action removeEntryAction = new RemoveEntryAction();
model = new ConfigDescriptionModel();
entryList = new JList(model);
entryList.addListSelectionListener(new ConfigListSelectionListener());
final JToolBar toolbar = new JToolBar();
toolbar.setFloatable(false);
toolbar.add(addEntryAction);
toolbar.add(removeEntryAction);
final JPanel panel = new JPanel();
panel.setMinimumSize(new Dimension(200, 0));
panel.setLayout(new BorderLayout());
panel.add(toolbar, BorderLayout.NORTH);
panel.add(new JScrollPane
(entryList, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED), BorderLayout.CENTER);
return panel;
}
/**
* Returns the JList component containing all entries of the enumeration detail editor.
*
* @return the enumeration entry list.
*/
protected JList getEnumEntryList()
{
return enumEntryList;
}
/**
* Returns the text field containing the currently edited enumeration entry.
*
* @return the textfield containing the current entry.
*/
protected JTextField getEnumEntryEditField()
{
return enumEntryEditField;
}
/**
* Returns the List Model containing all entries of the current enumeration entry editor.
*
* @return the entry list.
*/
protected DefaultListModel getEnumEntryListModel()
{
return enumEntryListModel;
}
/**
* Returns the JList component containing all configuration entries.
*
* @return the entry list.
*/
protected JList getEntryList()
{
return entryList;
}
/**
* Creates a panel containing all dialog control buttons, like close, load, save and import.
*
* @return the button panel.
*/
private JPanel createButtonPane()
{
final Action closeAction = new CloseAction();
final Action saveAction = new SaveAction();
final Action loadAction = new LoadAction();
final Action importAction = new ImportAction();
final JPanel panel = new JPanel();
panel.setLayout(new FlowLayout(FlowLayout.RIGHT));
panel.setBorder(new EmptyBorder(5, 5, 5, 5));
final JPanel buttonHolder = new JPanel();
buttonHolder.setLayout(new GridLayout(1, 4, 5, 5));
buttonHolder.add(new JButton(importAction));
buttonHolder.add(new JButton(loadAction));
buttonHolder.add(new JButton(saveAction));
buttonHolder.add(new JButton(closeAction));
panel.add(buttonHolder);
return panel;
}
/**
* Creates the detail editor panel. This panel will contain all specific editors for the keys.
*
* @return the detail editor panel.
*/
private JPanel createEditPane()
{
final JPanel buttonHolder = new JPanel();
buttonHolder.setLayout(new GridLayout(1, 4, 5, 5));
buttonHolder.add(new JButton(new CancelAction()));
buttonHolder.add(new JButton(new UpdateAction()));
final JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
buttonPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
buttonPanel.add(buttonHolder);
final JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(createDetailEditorPanel(), BorderLayout.CENTER);
panel.add(buttonPanel, BorderLayout.SOUTH);
return panel;
}
/**
* Creates the enumeration detail editor.
*
* @return the enumeration detail editor.
*/
private JPanel createEnumerationEditor()
{
enumEntryEditField = new JTextField();
enumEntryListModel = new DefaultListModel();
enumEntryList = new JList(enumEntryListModel);
enumEntryList.addListSelectionListener(new EnumerationListSelectionHandler());
final JPanel listPanel = new JPanel();
listPanel.setLayout(new BorderLayout());
listPanel.add(enumEntryEditField, BorderLayout.NORTH);
listPanel.add(new JScrollPane(enumEntryList), BorderLayout.CENTER);
final JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(5, 1));
buttonPanel.add(new JButton(new AddEnumEntryAction()));
buttonPanel.add(new JButton(new RemoveEnumEntryAction()));
buttonPanel.add(new JButton(new UpdateEnumEntryAction()));
buttonPanel.add(new JPanel());
buttonPanel.add(new JButton(new SetBooleanEnumEntryAction()));
final JPanel buttonCarrier = new JPanel();
buttonCarrier.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
buttonCarrier.add(buttonPanel);
final JPanel editorPanel = new JPanel();
editorPanel.setLayout(new BorderLayout());
editorPanel.add(listPanel, BorderLayout.CENTER);
editorPanel.add(buttonCarrier, BorderLayout.EAST);
return editorPanel;
}
/**
* Creates the class detail editor.
*
* @return the class detail editor.
*/
private JPanel createClassEditor()
{
baseClassField = new JTextField();
final JLabel baseClassValidateMessage = new JLabel(" ");
final JLabel textLabel = new JLabel
(resources.getString("config-description-editor.baseclass")); //$NON-NLS-1$
final JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(textLabel, BorderLayout.WEST);
panel.add(baseClassField, BorderLayout.CENTER);
panel.add(baseClassValidateMessage, BorderLayout.SOUTH);
final JPanel carrier = new JPanel();
carrier.setLayout(new BorderLayout());
carrier.add(panel, BorderLayout.NORTH);
return carrier;
}
/**
* Creates the text detail editor.
*
* @return the text detail editor.
*/
private JPanel createTextEditor()
{
final JLabel textLabel = new JLabel
(resources.getString("config-description-editor.text-editor-message")); //$NON-NLS-1$
final JPanel panel = new JPanel();
panel.setLayout(new FlowLayout());
panel.add(textLabel);
return panel;
}
/**
* Creates the common entry detail editor. This editor contains all shared properties.
*
* @return the common entry editor.
*/
private JPanel createDetailEditorPanel()
{
final JLabel keyNameLabel = new JLabel
(resources.getString("config-description-editor.keyname")); //$NON-NLS-1$
final JLabel descriptionLabel = new JLabel
(resources.getString("config-description-editor.description")); //$NON-NLS-1$
final JLabel typeLabel = new JLabel(resources.getString("config-description-editor.type")); //$NON-NLS-1$
final JLabel globalLabel = new JLabel(resources.getString("config-description-editor.global")); //$NON-NLS-1$
final JLabel hiddenLabel = new JLabel(resources.getString("config-description-editor.hidden")); //$NON-NLS-1$
hiddenField = new JCheckBox();
globalField = new JCheckBox();
final String font = ConfigEditorBoot.getInstance().getGlobalConfig().getConfigProperty
(ConfigDescriptionEditor.EDITOR_FONT_KEY, "Monospaced"); //$NON-NLS-1$
final int fontSize = ParserUtil.parseInt
(ConfigEditorBoot.getInstance().getGlobalConfig().getConfigProperty
(ConfigDescriptionEditor.EDITOR_FONT_SIZE_KEY), 12);
descriptionField = new JTextArea();
descriptionField.setFont(new Font(font, Font.PLAIN, fontSize));
descriptionField.setLineWrap(true);
descriptionField.setWrapStyleWord(true);
keyNameField = new JTextField();
final JPanel enumerationEditor = createEnumerationEditor();
final JPanel textEditor = createTextEditor();
final JPanel classEditor = createClassEditor();
detailManagerPanel = new JPanel();
detailManager = new CardLayout();
detailManagerPanel.setLayout(detailManager);
detailManagerPanel.add(classEditor, ConfigDescriptionEditor.CLASS_DETAIL_EDITOR_NAME);
detailManagerPanel.add(textEditor, ConfigDescriptionEditor.TEXT_DETAIL_EDITOR_NAME);
detailManagerPanel.add(enumerationEditor, ConfigDescriptionEditor.ENUM_DETAIL_EDITOR_NAME);
final JPanel commonEntryEditorPanel = new JPanel();
commonEntryEditorPanel.setLayout(new GridBagLayout());
commonEntryEditorPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.insets = new Insets(3, 1, 1, 1);
commonEntryEditorPanel.add(keyNameLabel, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(3, 1, 1, 1);
gbc.ipadx = 120;
commonEntryEditorPanel.add(keyNameField, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 1;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.insets = new Insets(3, 1, 1, 1);
commonEntryEditorPanel.add(descriptionLabel, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 1;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(3, 1, 1, 1);
gbc.ipadx = 120;
gbc.ipady = 120;
commonEntryEditorPanel.add(new JScrollPane
(descriptionField,
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER), gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 2;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.insets = new Insets(3, 1, 1, 1);
commonEntryEditorPanel.add(globalLabel, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 2;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(3, 1, 1, 1);
gbc.ipadx = 120;
commonEntryEditorPanel.add(globalField, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 3;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.insets = new Insets(3, 1, 1, 1);
commonEntryEditorPanel.add(hiddenLabel, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 3;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(3, 1, 1, 1);
gbc.ipadx = 120;
commonEntryEditorPanel.add(hiddenField, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 4;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.insets = new Insets(3, 1, 1, 1);
commonEntryEditorPanel.add(typeLabel, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 4;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(3, 1, 1, 1);
gbc.ipadx = 120;
commonEntryEditorPanel.add(createTypeSelectionPane(), gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 5;
gbc.gridwidth = 2;
gbc.weighty = 1;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.BOTH;
gbc.insets = new Insets(3, 1, 1, 1);
gbc.ipadx = 120;
commonEntryEditorPanel.add(detailManagerPanel, gbc);
return commonEntryEditorPanel;
}
/**
* Creates the type selection panel containing some radio buttons to define the detail editor type.
*
* @return the type selection panel.
*/
private JPanel createTypeSelectionPane()
{
final JPanel panel = new JPanel();
panel.setLayout(new GridLayout(3, 1));
rbText = new JRadioButton(new SelectTypeAction
(resources.getString("config-description-editor.type-text"), ConfigDescriptionEditor.TYPE_TEXT)); //$NON-NLS-1$
rbClass = new JRadioButton(new SelectTypeAction
(resources.getString("config-description-editor.type-class"),
ConfigDescriptionEditor.TYPE_CLASS)); //$NON-NLS-1$
rbEnum = new JRadioButton(new SelectTypeAction
(resources.getString("config-description-editor.type-enum"), ConfigDescriptionEditor.TYPE_ENUM)); //$NON-NLS-1$
final ButtonGroup bg = new ButtonGroup();
bg.add(rbText);
bg.add(rbClass);
bg.add(rbEnum);
panel.add(rbText);
panel.add(rbClass);
panel.add(rbEnum);
return panel;
}
/**
* Creates the statusbar for this frame. Use setStatus() to display text on the status bar.
*
* @return the status bar.
*/
protected JPanel createStatusBar()
{
final JPanel statusPane = new JPanel();
statusPane.setLayout(new BorderLayout());
statusPane.setBorder(BorderFactory.createLineBorder(UIManager.getDefaults().getColor(
"controlShadow"))); //$NON-NLS-1$
statusHolder = new JLabel(" "); //$NON-NLS-1$
statusPane.setMinimumSize(statusHolder.getPreferredSize());
statusPane.add(statusHolder, BorderLayout.WEST);
return statusPane;
}
/**
* Defines the status text for this dialog.
*
* @param text the new status text.
*/
protected void setStatusText(final String text)
{
statusHolder.setText(text);
}
/**
* Returns the currently visible status text of this dialog.
*
* @return the status text.
*/
protected String getStatusText()
{
return statusHolder.getText();
}
/**
* Sets the entry type for the current config description entry. This also selects and activates the correct detail
* editor for this type.
*
* @param type the type of the currently selected entry.
*/
protected void setEntryType(final int type)
{
this.type = type;
if (type == ConfigDescriptionEditor.TYPE_CLASS)
{
detailManager.show(detailManagerPanel, ConfigDescriptionEditor.CLASS_DETAIL_EDITOR_NAME);
rbClass.setSelected(true);
}
else if (type == ConfigDescriptionEditor.TYPE_ENUM)
{
detailManager.show(detailManagerPanel, ConfigDescriptionEditor.ENUM_DETAIL_EDITOR_NAME);
rbEnum.setSelected(true);
}
else
{
detailManager.show(detailManagerPanel, ConfigDescriptionEditor.TEXT_DETAIL_EDITOR_NAME);
rbText.setSelected(true);
}
invalidate();
}
/**
* Returns the current entry type.
*
* @return the current entry type.
*/
protected int getEntryType()
{
return type;
}
/**
* Returns the currently select entry from the entry list model.
*
* @return the currently selected entry.
*/
protected ConfigDescriptionEntry getSelectedEntry()
{
return selectedEntry;
}
/**
* Defines the currently selected entry from the entry list model and updates the detail editor to reflect the data
* from the entry.
*
* @param selectedEntry the selected entry.
*/
protected void setSelectedEntry(final ConfigDescriptionEntry selectedEntry)
{
this.selectedEntry = selectedEntry;
enumEntryEditField.setText(""); //$NON-NLS-1$
enumEntryListModel.clear();
baseClassField.setText(""); //$NON-NLS-1$
if (this.selectedEntry == null)
{
deepEnable(detailEditorPane, false);
}
else
{
deepEnable(detailEditorPane, true);
keyNameField.setText(selectedEntry.getKeyName());
globalField.setSelected(selectedEntry.isGlobal());
hiddenField.setSelected(selectedEntry.isHidden());
descriptionField.setText(selectedEntry.getDescription());
if (selectedEntry instanceof ClassConfigDescriptionEntry)
{
final ClassConfigDescriptionEntry ce = (ClassConfigDescriptionEntry) selectedEntry;
setEntryType(ConfigDescriptionEditor.TYPE_CLASS);
if (ce.getBaseClass() != null)
{
baseClassField.setText(ce.getBaseClass().getName());
}
}
else if (selectedEntry instanceof EnumConfigDescriptionEntry)
{
final EnumConfigDescriptionEntry en = (EnumConfigDescriptionEntry) selectedEntry;
final String[] enums = en.getOptions();
for (int i = 0; i < enums.length; i++)
{
enumEntryListModel.addElement(enums[i]);
}
setEntryType(ConfigDescriptionEditor.TYPE_ENUM);
}
else
{
setEntryType(ConfigDescriptionEditor.TYPE_TEXT);
}
}
}
/**
* A utility method to enable or disable a component and all childs.
*
* @param comp the component that should be enabled or disabled.
* @param state the new enable state.
*/
private void deepEnable(final Component comp, final boolean state)
{
comp.setEnabled(state);
if (comp instanceof Container)
{
final Container cont = (Container) comp;
final Component[] childs = cont.getComponents();
for (int i = 0; i < childs.length; i++)
{
deepEnable(childs[i], state);
}
}
}
/**
* Saves the config description model in a xml file.
*/
protected void save()
{
fileChooser.setVisible(true);
final int option = fileChooser.showSaveDialog(this);
if (option == JFileChooser.APPROVE_OPTION)
{
OutputStream out = null;
try
{
out = new BufferedOutputStream(new FileOutputStream(fileChooser.getSelectedFile()));
model.save(out, "ISO-8859-1"); //$NON-NLS-1$
out.close();
setStatusText(resources.getString("config-description-editor.save-complete")); //$NON-NLS-1$
}
catch (Exception ioe)
{
ConfigDescriptionEditor.logger.debug("Failed", ioe); //$NON-NLS-1$
final String message = MessageFormat.format
(resources.getString("config-description-editor.save-failed"), //$NON-NLS-1$
new Object[]{ioe.getMessage()});
setStatusText(message);
}
finally
{
if (out != null)
{
try
{
out.close();
}
catch (IOException e)
{
// ignored .. at least we tried it ..
}
}
}
}
}
/**
* Loads the config description model from a xml file.
*/
protected void load()
{
fileChooser.setVisible(true);
final int option = fileChooser.showOpenDialog(this);
if (option == JFileChooser.APPROVE_OPTION)
{
InputStream in = null;
try
{
final FileInputStream fileIn = new FileInputStream(fileChooser.getSelectedFile());
in = new BufferedInputStream(fileIn);
model.load(in);
model.sort();
setStatusText(resources.getString("config-description-editor.load-complete")); //$NON-NLS-1$
}
catch (Exception ioe)
{
ConfigDescriptionEditor.logger.debug("Load Failed", ioe); //$NON-NLS-1$
final String message = MessageFormat.format
(resources.getString("config-description-editor.load-failed"), //$NON-NLS-1$
new Object[]{ioe.getMessage()});
setStatusText(message);
}
finally
{
if (in != null)
{
try
{
in.close();
}
catch (IOException e)
{
// ignored .. at least we tried it ..
}
}
}
}
}
/**
* Updates the currently selected entry from the values found in the detail editor.
*/
protected void writeSelectedEntry()
{
final ConfigDescriptionEntry entry;
switch (getEntryType())
{
case ConfigDescriptionEditor.TYPE_CLASS:
{
final ClassConfigDescriptionEntry ce = new ClassConfigDescriptionEntry(keyNameField.getText());
ce.setDescription(descriptionField.getText());
ce.setGlobal(globalField.isSelected());
ce.setHidden(hiddenField.isSelected());
try
{
final String className = baseClassField.getText();
if (className == null)
{
ce.setBaseClass(Object.class);
}
else
{
final ClassLoader classLoader = ObjectUtilities.getClassLoader(getClass());
final Class c = Class.forName(className, false, classLoader);
ce.setBaseClass(c);
}
}
catch (Exception e)
{
// invalid
ConfigDescriptionEditor.logger.debug("Class is invalid; defaulting to Object.class"); //$NON-NLS-1$
ce.setBaseClass(Object.class);
}
entry = ce;
break;
}
case ConfigDescriptionEditor.TYPE_ENUM:
{
final EnumConfigDescriptionEntry ece = new EnumConfigDescriptionEntry(keyNameField.getText());
ece.setDescription(descriptionField.getText());
ece.setGlobal(globalField.isSelected());
ece.setHidden(hiddenField.isSelected());
final String[] enumEntries = new String[enumEntryListModel.getSize()];
for (int i = 0; i < enumEntryListModel.getSize(); i++)
{
enumEntries[i] = String.valueOf(enumEntryListModel.get(i));
}
ece.setOptions(enumEntries);
entry = ece;
break;
}
default:
{
final TextConfigDescriptionEntry te = new TextConfigDescriptionEntry(keyNameField.getText());
te.setDescription(descriptionField.getText());
te.setGlobal(globalField.isSelected());
te.setHidden(hiddenField.isSelected());
entry = te;
break;
}
}
final ConfigDescriptionEntry selectedEntry = getSelectedEntry();
if (selectedEntry == null)
{
model.add(entry);
return;
}
if (ObjectUtilities.equal(selectedEntry.getKeyName(), entry.getKeyName()) == false)
{
model.remove(selectedEntry);
}
model.add(entry);
}
/**
* Returns the config description model containing all metainformation about the configuration.
*
* @return the config description model.
*/
protected ConfigDescriptionModel getModel()
{
return model;
}
/**
* Handles the attemp to quit the program. This method shuts down the VM.
*/
protected void attempExit()
{
System.exit(0);
}
/**
* Returns the resource bundle of this editor for translating strings.
*
* @return the resource bundle.
*/
protected ResourceBundleSupport getResources()
{
return resources;
}
/**
* The main entry point to start the detail editor.
*
* @param args ignored.
*/
public static void main(final String[] args)
{
ConfigEditorBoot.getInstance().start();
if (args.length == 0)
{
final ConfigDescriptionEditor ed = new ConfigDescriptionEditor();
ed.pack();
ed.setVisible(true);
}
else
{
final AbstractBoot boot = AbstractBoot.loadBooter(args[0], ConfigDescriptionEditor.class);
if (boot == null)
{
System.out.println("Error: Unable to load the specified booter class: " + args[0]);
System.exit(-1);
}
final ConfigDescriptionEditor ed = new ConfigDescriptionEditor(boot);
ed.pack();
ed.setVisible(true);
}
}
}