/* * 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.HierarchicalConfiguration; import org.pentaho.reporting.libraries.base.util.FilesystemFilter; import org.pentaho.reporting.libraries.base.util.Messages; import org.pentaho.reporting.libraries.base.util.ObjectUtilities; import org.pentaho.reporting.libraries.base.util.ResourceBundleSupport; import org.pentaho.reporting.libraries.base.util.StringUtils; import org.pentaho.reporting.tools.configeditor.model.ConfigTreeModelException; import javax.swing.*; import javax.swing.border.EmptyBorder; 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.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.StringReader; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.Locale; import java.util.Properties; /** * The ConfigEditor can be used to edit the global jfreereport.properties files. These files provide global settings for * all reports and contain the system level configuration of JFreeReport. * * @author Thomas Morgner */ public class ConfigEditor extends JFrame { private static final Log logger = LogFactory.getLog( ConfigEditor.class ); /** * An Action to handle close requests. */ private class CloseAction extends AbstractAction { /** * DefaultConstructor. */ protected CloseAction() { putValue( Action.NAME, getResources().getString( "action.exit.name" ) ); //$NON-NLS-1$ } /** * Invoked when an action occurs. The action invokes System.exit(0). * * @param e the action event. */ public void actionPerformed( final ActionEvent e ) { attempClose(); } } /** * An action to handle save requests. */ private class SaveAction extends AbstractAction { /** * DefaultConstructor. */ protected 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$ } /** * Saves the configuration. * * @param e the action event. */ public void actionPerformed( final ActionEvent e ) { save(); } } /** * An action to handle load requests. */ private class LoadAction extends AbstractAction { /** * DefaultConstructor. */ protected 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$ } /** * Loads the configuration. * * @param e the action event. */ public void actionPerformed( final ActionEvent e ) { load(); } } /** * An action to handle new requests, which reset the report configuration. */ private class NewAction extends AbstractAction { /** * DefaultConstructor. */ protected NewAction() { putValue( Action.NAME, getResources().getString( "action.new.name" ) ); //$NON-NLS-1$ putValue( Action.SMALL_ICON, getResources().getIcon( "action.new.small-icon" ) ); //$NON-NLS-1$ } /** * Reset the configuration. * * @param e the action event. */ public void actionPerformed( final ActionEvent e ) { reset(); } } 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 ) { attempClose(); } } private static final String PROPERTIES_FILE_EXTENSION = ".properties"; /** * A constant defining that text should be escaped in a way which is suitable for property keys. */ private static final int ESCAPE_KEY = 0; /** * A constant defining that text should be escaped in a way which is suitable for property values. */ private static final int ESCAPE_VALUE = 1; /** * A constant defining that text should be escaped in a way which is suitable for property comments. */ private static final int ESCAPE_COMMENT = 2; /** * A label that serves as status bar. */ private JLabel statusHolder; /** * The resource bundle instance of this dialog. */ private final ResourceBundleSupport resources; /** * The file chooser used to load and save the report configuration. */ private JFileChooser fileChooser; private HierarchicalConfiguration configuration; private ConfigEditorPane editorPane; public ConfigEditor() throws ConfigTreeModelException { this( new HierarchicalConfiguration( ConfigEditorBoot.getInstance().getGlobalConfig() ), ConfigEditorBoot.getInstance() ); } public ConfigEditor( final HierarchicalConfiguration configuration, final AbstractBoot packageManager ) throws ConfigTreeModelException { this.configuration = configuration; resources = new ResourceBundleSupport( getLocale(), ConfigEditorBoot.BUNDLE_NAME, ObjectUtilities.getClassLoader( ConfigEditor.class ) ); editorPane = new ConfigEditorPane( packageManager, true ); editorPane.updateConfiguration( configuration ); setTitle( resources.getString( "config-editor.title" ) ); //$NON-NLS-1$ final JPanel contentPane = new JPanel(); contentPane.setLayout( new BorderLayout() ); contentPane.add( editorPane, 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 ); addWindowListener( new CloseHandler() ); } /** * Returns the resource bundle of this editor for translating strings. * * @return the resource bundle. */ protected ResourceBundleSupport getResources() { return resources; } /** * Creates the button pane to hold all control buttons. * * @return the created panel with all control buttons. */ private JPanel createButtonPane() { final Action closeAction = new CloseAction(); final Action saveAction = new SaveAction(); final Action loadAction = new LoadAction(); final Action newAction = new NewAction(); final JPanel buttonHolder = new JPanel(); buttonHolder.setLayout( new GridLayout( 1, 4, 5, 5 ) ); buttonHolder.add( new JButton( newAction ) ); buttonHolder.add( new JButton( loadAction ) ); buttonHolder.add( new JButton( saveAction ) ); buttonHolder.add( new JButton( closeAction ) ); final JPanel panel = new JPanel(); panel.setLayout( new FlowLayout( FlowLayout.RIGHT ) ); panel.setBorder( new EmptyBorder( 5, 5, 5, 5 ) ); panel.add( buttonHolder ); 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 text to be displayed on the status bar. Setting text will replace any other previously defined text. * * @param text the new statul bar text. */ private void setStatusText( final String text ) { statusHolder.setText( text ); } // private String getStatusText () // { // return statusHolder.getText(); // } /** * Loads the report configuration from a user selectable report properties file. */ protected void load() { setStatusText( resources.getString( "ConfigEditor.USER_LOADING_FILE" ) ); //$NON-NLS-1$ if ( fileChooser == null ) { fileChooser = new JFileChooser(); final FilesystemFilter filter = new FilesystemFilter ( ConfigEditor.PROPERTIES_FILE_EXTENSION, resources.getString( "config-editor.file-description.properties" ) ); //$NON-NLS-1$ fileChooser.addChoosableFileFilter( filter ); fileChooser.setMultiSelectionEnabled( false ); } final int option = fileChooser.showOpenDialog( this ); if ( option == JFileChooser.APPROVE_OPTION ) { final File selFile = fileChooser.getSelectedFile(); String selFileName = selFile.getAbsolutePath(); // Test if ends on .properties if ( StringUtils.endsWithIgnoreCase( selFileName, ConfigEditor.PROPERTIES_FILE_EXTENSION ) == false ) { selFileName = selFileName + ConfigEditor.PROPERTIES_FILE_EXTENSION; } final Properties prop = new Properties(); try { final InputStream in = new BufferedInputStream( new FileInputStream( selFileName ) ); try { prop.load( in ); } finally { in.close(); } } catch ( IOException ioe ) { ConfigEditor.logger.debug( resources.getString( "ConfigEditor.ERROR_0003_FAILED_TO_LOAD_PROPERTIES", ioe.toString() ), ioe ); //$NON-NLS-1$ setStatusText( resources.getString( "ConfigEditor.ERROR_0003_FAILED_TO_LOAD_PROPERTIES", ioe.getMessage() ) ); //$NON-NLS-1$ return; } reset(); final Enumeration keys = prop.keys(); while ( keys.hasMoreElements() ) { final String key = (String) keys.nextElement(); final String value = prop.getProperty( key ); configuration.setConfigProperty( key, value ); } editorPane.updateConfiguration( configuration ); setStatusText( resources.getString( "ConfigEditor.USER_LOAD_PROPS_COMPLETE" ) ); //$NON-NLS-1$ } } protected void reset() { editorPane.reset(); } /** * Saves the report configuration to a user selectable report properties file. */ protected void save() { setStatusText( resources.getString( "ConfigEditor.USER_SAVING" ) ); //$NON-NLS-1$ editorPane.commit(); if ( fileChooser == null ) { fileChooser = new JFileChooser(); final FilesystemFilter filter = new FilesystemFilter ( ConfigEditor.PROPERTIES_FILE_EXTENSION, resources.getString( "config-editor.file-description.properties" ) ); //$NON-NLS-1$ fileChooser.addChoosableFileFilter( filter ); fileChooser.setMultiSelectionEnabled( false ); } final int option = fileChooser.showSaveDialog( this ); if ( option == JFileChooser.APPROVE_OPTION ) { final File selFile = fileChooser.getSelectedFile(); String selFileName = selFile.getAbsolutePath(); // Test if ends on xls if ( StringUtils.endsWithIgnoreCase( selFileName, ConfigEditor.PROPERTIES_FILE_EXTENSION ) == false ) { selFileName = selFileName + ConfigEditor.PROPERTIES_FILE_EXTENSION; } write( selFileName ); } } /** * Writes the configuration into the file specified by the given file name. * * @param filename the target file name */ private void write( final String filename ) { final Properties prop = new Properties(); final ArrayList<String> names = new ArrayList<String>(); // clear all previously set configuration settings ... final Enumeration defaults = configuration.getConfigProperties(); while ( defaults.hasMoreElements() ) { final String key = (String) defaults.nextElement(); names.add( key ); prop.setProperty( key, configuration.getConfigProperty( key ) ); } Collections.sort( names ); PrintWriter out = null; try { out = new PrintWriter( new OutputStreamWriter( new BufferedOutputStream( new FileOutputStream( filename ) ) ) ); for ( int i = 0; i < names.size(); i++ ) { final String key = names.get( i ); final String value = prop.getProperty( key ); final String description = editorPane.getDescriptionForKey( key ); if ( description != null ) { writeDescription( description, out ); } saveConvert( key, ConfigEditor.ESCAPE_KEY, out ); out.print( "=" ); //$NON-NLS-1$ saveConvert( value, ConfigEditor.ESCAPE_VALUE, out ); out.println(); } out.close(); setStatusText( resources.getString( "ConfigEditor.USER_SAVING_COMPLETE" ) ); //$NON-NLS-1$ } catch ( IOException ioe ) { ConfigEditor.logger.debug( resources.getString( "ConfigEditor.ERROR_0004_FAILED_PROPERTIES_SAVE", ioe.toString() ), ioe ); //$NON-NLS-1$ setStatusText( resources.getString( "ConfigEditor.ERROR_0004_FAILED_PROPERTIES_SAVE", ioe.getMessage() ) ); //$NON-NLS-1$ } finally { if ( out != null ) { out.close(); } } } /** * Writes a descriptive comment into the given print writer. * * @param text the text to be written. If it contains more than one line, every line will be prepended by the * comment character. * @param writer the writer that should receive the content. * @noinspection NestedAssignment */ private void writeDescription( final String text, final PrintWriter writer ) { // check if empty content ... this case is easy ... if ( text.length() == 0 ) { return; } writer.println( "# " ); //$NON-NLS-1$ try { final BufferedReader br = new BufferedReader( new StringReader( text ) ); String s; while ( ( s = br.readLine() ) != null ) { writer.print( "# " ); //$NON-NLS-1$ saveConvert( s, ConfigEditor.ESCAPE_COMMENT, writer ); writer.println(); } br.close(); } catch ( IOException e ) { // does not happen, this is a string-reader } } /** * Performs the necessary conversion of an java string into a property escaped string. * * @param text the text to be escaped * @param escapeMode the mode that should be applied. * @param writer the writer that should receive the content. */ private void saveConvert( final String text, final int escapeMode, final PrintWriter writer ) { final char[] string = text.toCharArray(); final char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; for ( int x = 0; x < string.length; x++ ) { final char aChar = string[ x ]; switch( aChar ) { case ' ': { if ( ( escapeMode != ConfigEditor.ESCAPE_COMMENT ) && ( x == 0 || escapeMode == ConfigEditor.ESCAPE_KEY ) ) { writer.print( '\\' ); } writer.print( ' ' ); break; } case '\\': { writer.print( '\\' ); writer.print( '\\' ); break; } case '\t': { if ( escapeMode == ConfigEditor.ESCAPE_COMMENT ) { writer.print( aChar ); } else { writer.print( '\\' ); writer.print( 't' ); } break; } case '\n': { writer.print( '\\' ); writer.print( 'n' ); break; } case '\r': { writer.print( '\\' ); writer.print( 'r' ); break; } case '\f': { if ( escapeMode == ConfigEditor.ESCAPE_COMMENT ) { writer.print( aChar ); } else { writer.print( '\\' ); writer.print( 'f' ); } break; } case '#': case '"': case '!': case '=': case ':': { if ( escapeMode == ConfigEditor.ESCAPE_COMMENT ) { writer.print( aChar ); } else { writer.print( '\\' ); writer.print( aChar ); } break; } default: if ( ( aChar < 0x0020 ) || ( aChar > 0x007e ) ) { writer.print( '\\' ); writer.print( 'u' ); writer.print( hexChars[ ( aChar >> 12 ) & 0xF ] ); writer.print( hexChars[ ( aChar >> 8 ) & 0xF ] ); writer.print( hexChars[ ( aChar >> 4 ) & 0xF ] ); writer.print( hexChars[ aChar & 0xF ] ); } else { writer.print( aChar ); } } } } /** * Closes this frame and exits the JavaVM. */ protected void attempClose() { System.exit( 0 ); } /** * main Method to start the editor. * * @param args not used. */ public static void main( final String[] args ) { try { ConfigEditorBoot.getInstance().start(); final ConfigEditor ed = new ConfigEditor(); ed.pack(); ed.setVisible( true ); } catch ( Exception e ) { final Messages messages = new Messages ( Locale.getDefault(), ConfigEditorBoot.BUNDLE_NAME, ObjectUtilities.getClassLoader( ConfigEditorBoot.class ) ); final String message = messages.getString( "ConfigEditor.ERROR_0001_FAILED_TO_INITIALIZE" ); //$NON-NLS-1$ logger.debug( message, e ); JOptionPane.showMessageDialog( null, message ); } } }