/******************************************************************************* * Copyright (c) 2004, 2010 BREDEX GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * BREDEX GmbH - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.jubula.client.core.preferences.database; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.collections.BidiMap; import org.apache.commons.collections.bidimap.DualHashBidiMap; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; import org.eclipse.core.runtime.Platform; import org.eclipse.jubula.client.core.Activator; import org.eclipse.jubula.client.core.i18n.Messages; import org.eclipse.jubula.client.core.persistence.DatabaseConnectionInfo; import org.eclipse.osgi.util.NLS; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility methods for converting Database Connection preferences * to/from Strings. * * @author BREDEX GmbH * @created 02.02.2011 */ public class DatabaseConnectionConverter { /** ID of preference containing configured database connections */ public static final String PREF_DATABASE_CONNECTIONS = "org.eclipse.jubula.client.preference.databaseConnections"; //$NON-NLS-1$ /** * bidirectional mapping from a database connection type identifier * (String) to the corresponding connection info class (Class) */ public static final BidiMap CONNECTION_CLASS_LOOKUP = new DualHashBidiMap(); static { // these values are used for storing / retrieving Database Connection // preferences, so change them with care CONNECTION_CLASS_LOOKUP.put("H2", H2ConnectionInfo.class); //$NON-NLS-1$ CONNECTION_CLASS_LOOKUP.put("Oracle", OracleConnectionInfo.class); //$NON-NLS-1$ CONNECTION_CLASS_LOOKUP.put("PostGreSQL", PostGreSQLConnectionInfo.class); //$NON-NLS-1$ CONNECTION_CLASS_LOOKUP.put("MySQL", MySQLConnectionInfo.class); //$NON-NLS-1$ } /** the logger */ private static final Logger LOG = LoggerFactory.getLogger(DatabaseConnectionConverter.class); /** string for delimiting serialized Database Connections */ private static final String CONNECTION_SEPARATOR = "\n\n"; //$NON-NLS-1$ /** string for splitting serialized Database Connections */ private static final String CONNECTION_SPLIT_REGEX = Pattern.quote(CONNECTION_SEPARATOR); /** string for delimiting serialized Database Connection properties */ private static final String PROPERTY_SEPARATOR = "\n"; //$NON-NLS-1$ /** string for splitting serialized Database Connection properties */ private static final String PROPERTY_SPLIT_REGEX = Pattern.quote(PROPERTY_SEPARATOR); /** * Private constructor for utility class. */ private DatabaseConnectionConverter() { // nothing to initialize } /** * * @return the 0..n Database Connections found in the Preferences. */ public static List<DatabaseConnection> computeAvailableConnections() { return DatabaseConnectionConverter.convert( Platform.getPreferencesService().getString( Activator.PLUGIN_ID, DatabaseConnectionConverter.PREF_DATABASE_CONNECTIONS, StringUtils.EMPTY, null)); } /** * * @param preferenceValue String representation of * 0..n Database Connections. * @return the 0..n Database Connections represented by the given * parameter. */ public static List<DatabaseConnection> convert(String preferenceValue) { List<DatabaseConnection> connectionList = new LinkedList<DatabaseConnection>(); if (StringUtils.isNotBlank(preferenceValue)) { for (String connection : preferenceValue.split(CONNECTION_SPLIT_REGEX)) { String[] connInfo = connection.split(PROPERTY_SPLIT_REGEX); if (connInfo.length < 2 || connInfo.length % 2 != 0) { // either there are not enough entries, or the number of // property names and values do not match LOG.error(NLS.bind( Messages.DatabaseConnectionInvalidPreferenceString, connection)); continue; } Map<String, Object> beanProps = new HashMap<String, Object>(); for (int i = 2; i < connInfo.length; i = i + 2) { beanProps.put(connInfo[i], connInfo[i + 1]); } Class<? extends DatabaseConnectionInfo> infoClass = (Class)CONNECTION_CLASS_LOOKUP.get(connInfo[0]); if (infoClass == null) { // no corresponding class could be found for the // connection type LOG.error(NLS.bind( Messages.DatabaseConnectionInvalidPreferenceString, connection)); continue; } try { DatabaseConnectionInfo infoBean = infoClass.newInstance(); BeanUtils.populate(infoBean, beanProps); connectionList.add( new DatabaseConnection(connInfo[1], infoBean)); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (InstantiationException e) { throw new RuntimeException(e); } } } return connectionList; } /** * * @param elements 0..n Database Connections. * @return String representation of the given Database Connections. */ public static String convert( DatabaseConnection[] elements) { return serializeDatabaseList(elements); } /** * * @param connections The connections to serialize. * @return a String containing all of the information provided in the * method argument. */ private static String serializeDatabaseList( DatabaseConnection[] connections) { StringBuilder sb = new StringBuilder(); for (DatabaseConnection conn : connections) { sb.append(CONNECTION_CLASS_LOOKUP.getKey( conn.getConnectionInfo().getClass())); sb.append(PROPERTY_SEPARATOR); sb.append(serialize(conn)); sb.append(CONNECTION_SEPARATOR); } return sb.toString(); } /** * * @param connection The connection to represent as a string. Must not be * <code>null</code>. * @return a persistable String representation of the given object. */ private static String serialize(DatabaseConnection connection) { Validate.notNull(connection); StringBuilder sb = new StringBuilder(); sb.append(connection.getName()).append(PROPERTY_SEPARATOR); for (PropertyDescriptor propDesc : PropertyUtils.getPropertyDescriptors( connection.getConnectionInfo())) { String propName = propDesc.getName(); try { // only save writable properties, as we will not be able to // set read-only properties when reading connection info back // in from the preference store if (PropertyUtils.isWriteable( connection.getConnectionInfo(), propName)) { sb.append(propName).append(PROPERTY_SEPARATOR) .append(BeanUtils.getProperty( connection.getConnectionInfo(), propName)) .append(PROPERTY_SEPARATOR); } } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } sb.deleteCharAt(sb.length() - 1); return sb.toString(); } }