/* * Copyright (c) 2008, SQL Power Group Inc. * * This file is part of SQL Power Library. * * SQL Power Library is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * SQL Power Library 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * Created on Jun 28, 2005 * * This code belongs to SQL Power. */ package ca.sqlpower.sql; /** * The SPDataSource represents a database that the Power Loader or * the Architect can connect to. It holds all the information required for * making JDBC, ODBC, or native Oracle connections (depending on what type * of database the connection is for). * * @see ca.sqlpower.architect.PlDotIni * @author jack, jonathan */ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; public abstract class SPDataSource implements Comparable<SPDataSource> { private static final Logger logger = Logger.getLogger(SPDataSource.class); /** * This will return a more user friendly name to a class type. This way we can * define a more descriptive name for classes that extends SPDataSource. */ public static String getUserFriendlyName(Class<? extends SPDataSource> dsType) { if (dsType.equals(JDBCDataSource.class)) { return "Database connection"; } else if (dsType.equals(Olap4jDataSource.class)) { return "OLAP connection"; } else { return dsType.getSimpleName(); } } /** * This is the logical name, and also the display name, key in the map. */ public static final String PL_LOGICAL = "Logical"; /** * JDBC driver pathname prefix that says to look for a JAR file resource on * the classpath. */ public static final String BUILTIN = "builtin:"; /** * These are the actual properties that appear in the file for this data source. * The getters for various properties will consult the parent type where appropriate * (for example, when a value is missing from this map). */ private Map<String,String> properties; /** * The collection of data sources that this data source belongs to. */ private final DataSourceCollection<SPDataSource> parentCollection; /** * This field is transient; don't access it directly becuase it * will disappear when this instance is serialized. */ private transient PropertyChangeSupport pcs; /** * JDBC driver pathname prefix that says to look for a JAR file resource on * the remote SQL Power Enterprise server we're attached to. */ public static final String SERVER = "server:"; /** * Returns this DataSource's property change support, creating * a new one if necessary. */ private PropertyChangeSupport getPcs() { if (pcs == null) pcs = new PropertyChangeSupport(this); return pcs; } /** * Creates a new SPDataSource with a blank parent type and all other properties set to null. */ public SPDataSource(DataSourceCollection<SPDataSource> parentCollection) { properties = new LinkedHashMap<String,String>(); this.parentCollection = parentCollection; } /** * Copy constructor. Creates a semi-independent copy of the given data source. * <p> * This is for testing only! The ramifications of using this constructor * in production have not been thought through! * * @param copyMe the SPDataSource to make a copy of. */ public SPDataSource(SPDataSource copyMe) { properties = new LinkedHashMap<String, String>(copyMe.properties); parentCollection = copyMe.parentCollection; } /** * The method that actually modifies the property map. * * @param key The key to use in the map (this will be a PL.INI property name) * @param value The value that corresponds with the key * @param propertyName The name of the Java Beans property that changed. This will * be the property name in the resulting PropertyChangeEvent. * @return The old value of the property. */ protected String putImpl(String key, String value, String propertyName) { String oldValue = get(key); properties.put(key, value); getPcs().firePropertyChange(propertyName, oldValue, value); return oldValue; } /** * Adds the given key to the map. * * @param key The key to use. * @param value The value to associate with key. * @return The old value of the property. */ public String put(String key, String value) { return putImpl(key, value, key); } /** * Returns the value associated with the given key * * @param key * The key to use * @return The value associated with the given key, or null if no such value * exists */ public String get(String key) { return properties.get(key); } /** * Returns a read-only view of this data source's properties. */ public Map<String,String> getPropertiesMap() { return Collections.unmodifiableMap(properties); } /** * Compares all properties of this data source to those of the other. * If there are any differences, returns false. Otherwise, returns true. */ @Override public boolean equals(Object o) { if (o == null) return false; if (!(o instanceof SPDataSource)) return false; SPDataSource other = (SPDataSource) o; return this.properties.equals(other.properties); } /** * Returns a hash that depends on all property values. */ @Override public int hashCode() { return properties.hashCode(); } /** * Performs the comparison based on the name. The name is stored in * the {@link #PL_LOGICAL} in the properties map. This does not compare * all of the fields compared in {@link #equals(Object)}. * * @param o the SPDataSource object to compare with. * @return <0 if this data source comes before o; 0 if they * are equal; >0 otherwise. * @throws NullPointerException if o==null * @throws ClassCastException if o is not an instance of SPDataSource */ public final int compareTo(SPDataSource ds2) { if (this == ds2) return 0; int tmp; String v1, v2; v1 = getName(); v2 = ds2.getName(); if (v1 == null && v2 != null) return -1; else if (v1 != null && v2 == null) return 1; else if (v1 != null && v2 != null) { tmp = v1.compareToIgnoreCase(v2); } else { tmp = 0; } return tmp; } // --------------------- property change --------------------------- /** * Registers the given object as a listener to property changes on this * SPDataSource. */ public void addPropertyChangeListener(PropertyChangeListener l) { getPcs().addPropertyChangeListener(l); } /** * Removes the given object from the listener list, if it was on that list. * Does nothing if l is not a property change listener of this data source. */ public void removePropertyChangeListener(PropertyChangeListener l) { getPcs().removePropertyChangeListener(l); } /** * Returns an unmodifiable view of the list of property change listeners. */ public List<PropertyChangeListener> getPropertyChangeListeners() { return Collections.unmodifiableList(Arrays.asList(pcs.getPropertyChangeListeners())); } // ------------------- accessors and mutators for actual instance variables ------------------------ /** * Copies all properties from the given data source into this one. * After this method returns, this data source will specify the same * target database as the given data source. * * @param dbcs The connection spec to copy from (must not be null). */ public void copyFrom(SPDataSource dbcs) { properties.clear(); // This is extremely, stupidly cheap. The tree doesn't notice the change unless there's // a property change event for the data source's name. setName(dbcs.getName()); for (Map.Entry<String, String> entry : dbcs.getPropertiesMap().entrySet()) { // this is non-ideal, because the property change events will not have correct property names put(entry.getKey(), entry.getValue()); } } /** * Returns the data source collection that this data source belongs to. */ public DataSourceCollection<SPDataSource> getParentCollection() { return parentCollection; } /** * Gets the value of name * * @return the value of name */ public String getName() { return get(PL_LOGICAL); } /** * Sets the value of name. * @param argName Value to assign to this.name */ public void setName(String argName){ putImpl(PL_LOGICAL, argName, "name"); } /** * Gets the value of displayName * * @return the value of displayName */ public String getDisplayName() { return get(PL_LOGICAL); } /** * Sets the value of displayName * * @param argDisplayName Value to assign to this.displayName */ public void setDisplayName(String argDisplayName){ putImpl(PL_LOGICAL, argDisplayName, "name"); } }