gt:       put( node, TYPE_KEY, "local" );
context: /*
   * 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) 2007 - 2009 Pentaho Corporation..  All rights reserved.
   */
  
  package org.pentaho.reporting.ui.datasources.jdbc.connection;
  
  import java.util.Iterator;
  import java.util.Map;
  import java.util.Properties;
  import java.util.TreeMap;
  import java.util.prefs.BackingStoreException;
  import java.util.prefs.Preferences;
  
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.pentaho.reporting.libraries.base.util.StringUtils;
  
  /**
   * Manages the list of local database connections for this application.
   * <p/>
   * The list of local connections is contained in the user preferences for this user. The structure of the preferences is
   * as follows:
   * <ul>
   * <li>One node per connection - the name of the node is the JNDI Connection name
   * <li>Under each node, the following set of string keys and values which define each connection
   * <ul>
   * <li>driver - the JDBC driver class
   * <li>url - the URL of the database
   * <li>username - the username used to connect to the database
   * <li>password - the password used to connect to the database
   * </ul>
   * </ul>
   */
  public class JdbcConnectionDefinitionManager {
    // Logger
    private static final Log log = LogFactory.getLog( JdbcConnectionDefinitionManager.class );
  
    // The default connection (used if no other connections can be loaded)
    private static final JdbcConnectionDefinition SAMPLE_DATA_JNDI_SOURCE = new JndiConnectionDefinition( "SampleData",
        "SampleData", "Hypersonic", null, null );
    private static final JdbcConnectionDefinition SAMPLE_DATA_DRIVER_SOURCE = new DriverConnectionDefinition(
        "SampleData (Hypersonic)", "org.hsqldb.jdbcDriver", "jdbc:hsqldb:hsql://localhost:9001/sampledata",
        "pentaho_user", "password" );
    private static final JdbcConnectionDefinition SAMPLE_DATA_MEMORY_SOURCE = new DriverConnectionDefinition(
        "SampleData (Memory)", "org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:SampleData", "pentaho_user", "password" );
    private static final JdbcConnectionDefinition LOCAL_SAMPLE_DATA_DRIVER_SOURCE = new DriverConnectionDefinition(
        "SampleData (Local)", "org.hsqldb.jdbcDriver", "jdbc:hsqldb:./resources/sampledata/sampledata", "pentaho_user",
        "password" );
    private static final JdbcConnectionDefinition MYSQL_SAMPLE_DATA_DRIVER_SOURCE = new DriverConnectionDefinition(
        "SampleData (MySQL)", "com.mysql.jdbc.Driver", "jdbc:mysql://localhost:3306/sampledata", "pentaho_user",
        "password" );
  
    // The location in the user preferences tree for the JNDI datasource settings
    private static final String DATASOURCE_PREFERENCES_NODE = "org/pentaho/reporting/ui/datasources/jdbc/Settings";
  
    // The current set of Sources
    private TreeMap<String, JdbcConnectionDefinition> connectionDefinitions =
        new TreeMap<String, JdbcConnectionDefinition>();
  
    /**
     * The reference to the preferences object for the datasource settings
     */
    private final Preferences userPreferences;
  
    private static final String TYPE_KEY = "type";
    // Constants use in the storage of the field information
    private static final String DRIVER_KEY = "driver";
    private static final String URL_KEY = "url";
    private static final String USERNAME_KEY = "username";
    private static final String PASSWORD_KEY = "password";
    private static final String HOSTNAME_KEY = "hostname";
    private static final String PORT_KEY = "port";
    private static final String DATABASE_TYPE_KEY = "database_type";
    private static final String DATABASE_NAME_KEY = "database_name";
    private static final String JNDI_LOCATION = "jndi-location";
  
    /**
     * Initializes the instance by loading the database connection information from the configuration storage.
     */
    public JdbcConnectionDefinitionManager() {
      this( DATASOURCE_PREFERENCES_NODE );
    }
  
    public JdbcConnectionDefinitionManager( final String node ) {
      this( Preferences.userRoot().node( node ), node );
    }
  
    /**
     * package-local visibility for testing purposes
     */
    JdbcConnectionDefinitionManager( final Preferences externalPreferences, final String node ) {
      userPreferences = externalPreferences;
      // Load the list of JNDI Sources
      try {
        final String[] childNodeNames = userPreferences.childrenNames();
        for ( int i = 0; i < childNodeNames.length; i++ ) {
          final String name = childNodeNames[i];
          final Preferences p = userPreferences.node( name );
          final String type = p.get( "type", null );
          if ( type == null ) {
            p.removeNode();
          } else if ( type.equals( "local" ) ) {
            final Properties props = new Properties();
            if ( p.nodeExists( "properties" ) ) {
              final Preferences preferences = p.node( "properties" );
              final String[] strings = preferences.keys();
              for ( int j = 0; j < strings.length; j++ ) {
                final String string = strings[j];
                final String value = preferences.get( string, null );
                if ( value != null ) {
                  props.setProperty( string, value );
                } else {
                  props.remove( string );
                }
              }
            }
  
            final DriverConnectionDefinition driverConnection =
                new DriverConnectionDefinition( name, p.get( DRIVER_KEY, null ), p.get( URL_KEY, null ), p.get(
                    USERNAME_KEY, null ), p.get( PASSWORD_KEY, null ), p.get( HOSTNAME_KEY, null ),
                    p.get(DATABASE_NAME_KEY, null ), p.get( DATABASE_TYPE_KEY, null ), p.get( PORT_KEY, null ), props );
  
            connectionDefinitions.put( name, driverConnection );
          } else if ( type.equals( "jndi" ) ) {
            final JndiConnectionDefinition connectionDefinition =
                new JndiConnectionDefinition( name, p.get( JNDI_LOCATION, null ), p.get( DATABASE_TYPE_KEY, null ), p
                    .get( USERNAME_KEY, null ), p.get( PASSWORD_KEY, null ) );
            connectionDefinitions.put( name, connectionDefinition );
          } else {
            p.removeNode();
          }
        }
      } catch ( BackingStoreException e ) {
        // The system preferences system is not working - log this as a message and use defaults
        log.warn( "Could not access the user prefererences while loading the "
            + "JNDI connection information - using default JNDI connection entries", e );
      } catch ( final Exception e ) {
        log.warn( "Configuration information was invalid.", e );
      }
  
      // If the connectionDefinitions is empty, add any default entries
      if ( connectionDefinitions.isEmpty() && DATASOURCE_PREFERENCES_NODE.equals( node ) ) {
        if ( userPreferences.getBoolean( "sample-data-created", false ) == true ) {
          // only create the sample connections once, if we work on a totally fresh config.
          return;
        }
        updateSourceList( SAMPLE_DATA_JNDI_SOURCE );
        updateSourceList( SAMPLE_DATA_DRIVER_SOURCE );
        updateSourceList( SAMPLE_DATA_MEMORY_SOURCE );
        updateSourceList( LOCAL_SAMPLE_DATA_DRIVER_SOURCE );
        updateSourceList( MYSQL_SAMPLE_DATA_DRIVER_SOURCE );
        userPreferences.putBoolean( "sample-data-created", true );
        try {
          userPreferences.flush();
        } catch ( BackingStoreException e ) {
          // ignored ..
        }
      }
    }
  
    /**
     * Returns a copy of the current list of JNDI sources
     */
    public JdbcConnectionDefinition[] getSources() {
      return connectionDefinitions.values().toArray( new JdbcConnectionDefinition[connectionDefinitions.size()] );
    }
  
    /**
     * Removes the specified connection-definition from the list of connections.
     * 
     * @param name
     *          the name of the JNDI source to remove from the list
     */
    public void removeSource( final String name ) {
      // Make sure the name provided is not null
      if ( StringUtils.isEmpty( name ) ) {
        throw new IllegalArgumentException( "The provided name is invalid" );
      }
  
      // If the source is in our list, remove it
      connectionDefinitions.remove( name );
  
      // Remove the entry from the user preferences
      try {
        final Preferences node = userPreferences.node( name );
        if ( node != null ) {
          node.removeNode();
          userPreferences.flush();
        }
      } catch ( BackingStoreException e ) {
        log.error( "Could not remove JNDI connection entry [" + name + ']', e );
      }
  
    }
  
    /**
     * Adds or updates the source list with the specified source entry. Delagates to JNDI or JDBC method
     * 
     * @param source
     *          the entry to add/update in the list
     * @throws IllegalArgumentException
     *           indicates the source is <code>null</code>
     */
    public boolean updateSourceList( final JdbcConnectionDefinition source ) {
      if ( source == null ) {
        throw new IllegalArgumentException( "The provided source is null" );
      }
  
      if ( source instanceof DriverConnectionDefinition ) {
        return updateSourceList( (DriverConnectionDefinition) source );
      }
  
      if ( source instanceof JndiConnectionDefinition ) {
        return updateSourceList( (JndiConnectionDefinition) source );
      } else {
        throw new IllegalArgumentException( "The provided source is not a supported type" );
      }
    }
  
    /**
     * Adds or updates the source list with the specified source entry. If the entry exists (has the same name), the new
     * entry will replace the old entry. If the enrty does not already exist, the new entry will be added to the list. <br>
     * Since the definition of the ConnectionDefintion ensures that it will be valid, no testing will be performed on the
     * contents of the ConnectionDefintion.
     * 
     * @param source
     *          the entry to add/update in the list
     * @throws IllegalArgumentException
     *           indicates the source is <code>null</code>
     */
    private boolean updateSourceList( final DriverConnectionDefinition source ) throws IllegalArgumentException {
      if ( source == null ) {
        throw new IllegalArgumentException( "The provided source is null" );
      }
  
      // Update the node in the list
      final boolean updateExisting = ( connectionDefinitions.put( source.getName(), source ) != null );
  
      // Update the information in the preferences
      try {
        final Preferences node = userPreferences.node( source.getName() );
  