/* * 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 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved. */ package org.pentaho.reporting.tools.configeditor; 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; import javax.swing.*; import javax.swing.border.EmptyBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.*; 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; /** * 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 = 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, "UTF-8" ); //$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. */ @SuppressWarnings( "UseOfSystemOutOrSystemErr" ) 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 ); } } }