blob: 66565faa63b1dfa4874c9bfe6ead2f32a7ff563a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 1998, 2012 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.db.relational;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;
import org.eclipse.persistence.sessions.Connector;
import org.eclipse.persistence.sessions.DatabaseSession;
import org.eclipse.persistence.sessions.Project;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.tools.db.relational.handles.MWHandle.NodeReferenceScrubber;
import org.eclipse.persistence.tools.db.relational.platformsmodel.DatabasePlatform;
import org.eclipse.persistence.tools.db.relational.spi.ExternalDatabase;
import org.eclipse.persistence.tools.db.relational.spi.ExternalDatabaseFactory;
import org.eclipse.persistence.tools.db.relational.spi.ExternalTableDescription;
import org.eclipse.persistence.tools.db.relational.spi.jdbc.JDBCExternalDatabaseFactory;
import org.eclipse.persistence.tools.schemaframework.SchemaManager;
import org.eclipse.persistence.tools.utility.StringTools;
import org.eclipse.persistence.tools.utility.collection.CollectionTools;
import org.eclipse.persistence.tools.utility.iterable.LiveCloneIterable;
import org.eclipse.persistence.tools.utility.iterator.ArrayIterator;
import org.eclipse.persistence.tools.utility.iterator.TransformationIterator;
import org.eclipse.persistence.tools.utility.node.Node;
/**
* @version 2.6
*/
@SuppressWarnings("nls")
public final class ELDatabase extends ELModel {
/** the database platform should never be null */
private volatile DatabasePlatform databasePlatform;
public static final String DATABASE_PLATFORM_PROPERTY = "databasePlatform" ;
private Collection<ELLoginSpec> loginSpecs;
public static final String LOGIN_SPECS_COLLECTION = "loginSpecs";
private ELLoginSpecHandle deploymentLoginSpecHandle;
public static final String DEPLOYMENT_LOGIN_SPEC_PROPERTY = "deploymentLoginSpec";
private ELLoginSpecHandle developmentLoginSpecHandle;
public static final String DEVELOPMENT_LOGIN_SPEC_PROPERTY = "developmentLoginSpec";
private Collection<ELTable> tables;
public static final String TABLES_COLLECTION = "tables";
/**
* the "external" database that supplies the
* "external" tables used to build MWTables
*/
private volatile ExternalDatabase externalDatabase;
/**
* transient - java.sql.Driver used to connect to the database;
* this field is always null when executing under jdev
*/
private volatile Driver driver;
/**
* transient - java.sql.Connection used for collecting meta-data;
* this field is always null when executing under jdev
*/
private volatile Connection connection;
// virtual property
public static final String CONNECTED_PROPERTY = "connected";
/**
* transient - org.eclipse.persistence.tools.schemaframework.SchemaManager
* used for generating tables on the database;
* this field is always null when executing under jdev
*/
private volatile SchemaManager schemaManager;
private volatile ExternalDatabaseFactory dbFactory;
/**
* these table names are read in by TopLink and are then
* used and managed by the IOManager;
* DO NOT use them for anything else ~bjv
*/
private Collection<String> tableNames;
private static final String TABLE_NAMES_COLLECTION = "tableNames";
/** this setting queried reflectively by the I/O Manager; so don't remove it */
private static final String SUB_DIRECTORY_NAME = "tables";
// ********** constructors **********
public ELDatabase(DatabasePlatform databasePlatform) {
super(null);
this.databasePlatform = databasePlatform;
}
// ********** initialization **********
@Override
protected void checkParent(Node parentNode) {
// no-op since db is the root node
}
/**
* initialize transient state
*/
@Override
public void initialize() {
super.initialize();
// the tables are not mapped directly
this.tables = new Vector<ELTable>();
}
/**
* initialize persistent state
*/
@Override
protected void initialize(Node parent) {
super.initialize(parent);
this.loginSpecs = new Vector<ELLoginSpec>();
// 'deploymentLoginSpec' and 'developmentLoginSpec'
// are handled directly in #loginSpecRemoved(MWLoginSpec)
this.deploymentLoginSpecHandle = new ELLoginSpecHandle(this, NodeReferenceScrubber.NULL_INSTANCE);
this.developmentLoginSpecHandle = new ELLoginSpecHandle(this, NodeReferenceScrubber.NULL_INSTANCE);
this.tableNames = new HashSet<String>();
}
@Override
public Validator getValidator() {
return new Validator() {
@Override
public void validate() {
}
@Override
public void resume() {
}
@Override
public void pause() {
}
};
}
// ********** database platform **********
public DatabasePlatform getDatabasePlatform() {
return this.databasePlatform;
}
public void setDatabasePlatform(DatabasePlatform databasePlatform) {
if (databasePlatform == null) {
throw new NullPointerException();
}
Object old = this.databasePlatform;
this.databasePlatform = databasePlatform;
this.firePropertyChanged(DATABASE_PLATFORM_PROPERTY, old, databasePlatform);
if (this.attributeValueHasChanged(old, databasePlatform)) {
this.databasePlatformChanged();
}
}
/**
* cascade to the database fields so they can update their types
*/
private void databasePlatformChanged() {
synchronized (this.tables) {
for (ELTable table : this.tables) {
table.databasePlatformChanged();
}
}
}
// ********** login specs **********
public Iterable<ELLoginSpec> loginSpecs() {
return new LiveCloneIterable<ELLoginSpec>(this.loginSpecs) {
@Override
protected void remove(ELLoginSpec current) {
ELDatabase.this.removeLoginSpec(current);
}
};
}
public int loginSpecsSize() {
return this.loginSpecs.size();
}
public ELLoginSpec addLoginSpec(String loginSpecName) {
this.checkLoginSpecName(loginSpecName);
return this.addLoginSpec(new ELLoginSpec(this, loginSpecName));
}
private ELLoginSpec addLoginSpec(ELLoginSpec loginSpec) {
this.addItemToCollection(loginSpec, this.loginSpecs, LOGIN_SPECS_COLLECTION);
if (this.loginSpecs.size() == 1) {
this.setDeploymentLoginSpec(loginSpec);
this.setDevelopmentLoginSpec(loginSpec);
}
return loginSpec;
}
public void removeLoginSpec(ELLoginSpec loginSpec) {
if (this.removeItemFromCollection(loginSpec, this.loginSpecs, LOGIN_SPECS_COLLECTION)) {
this.loginSpecRemoved(loginSpec);
}
}
public boolean containsLoginSpecNamed(String loginSpecName) {
return this.loginSpecNamed(loginSpecName) != null;
}
public ELLoginSpec loginSpecNamed(String loginSpecName) {
synchronized (this.loginSpecs) {
for (ELLoginSpec spec : this.loginSpecs) {
if (spec.getName().equals(loginSpecName)) {
return spec;
}
}
}
return null;
}
public Iterator<String> loginSpecNames() {
return new TransformationIterator<ELLoginSpec, String>(this.loginSpecs()) {
@Override
protected String transform(ELLoginSpec next) {
return next.getName();
}
};
}
// ********** deployment login spec **********
public ELLoginSpec getDeploymentLoginSpec() {
return this.deploymentLoginSpecHandle.getLoginSpec();
}
public void setDeploymentLoginSpec(ELLoginSpec loginSpec) {
Object old = this.deploymentLoginSpecHandle.getLoginSpec();
this.deploymentLoginSpecHandle.setLoginSpec(loginSpec);
this.firePropertyChanged(DEPLOYMENT_LOGIN_SPEC_PROPERTY, old, loginSpec);
}
// ********** development login spec **********
public ELLoginSpec getDevelopmentLoginSpec() {
return this.developmentLoginSpecHandle.getLoginSpec();
}
public void setDevelopmentLoginSpec(ELLoginSpec loginSpec) {
Object old = this.developmentLoginSpecHandle.getLoginSpec();
this.developmentLoginSpecHandle.setLoginSpec(loginSpec);
this.firePropertyChanged(DEVELOPMENT_LOGIN_SPEC_PROPERTY, old, loginSpec);
}
// ********** tables **********
public Iterable<ELTable> tables() {
return new LiveCloneIterable<ELTable>(this.tables) {
@Override
protected void remove(ELTable current) {
ELDatabase.this.removeTable(current);
}
};
}
public int tablesSize() {
return this.tables.size();
}
public ELTable addTable(String shortName) {
return this.addTable(null, shortName);
}
public ELTable addTable(String schema, String shortName) {
return this.addTable(null, schema, shortName);
}
public ELTable addTable(String catalog, String schema, String shortName) {
this.checkTableName(catalog, schema, shortName, null);
return this.addTable(new ELTable(this, catalog, schema, shortName));
}
public ELTable addTableWithFullyQualifiedName(String fullyQualifiedName) {
Collection<String> strings = CollectionTools.collection(fullyQualifiedName.split("\\."));
CollectionTools.removeAllOccurrences(strings, StringTools.EMPTY_STRING);
String[] parsedName = strings.toArray(new String[strings.size()]);
if (parsedName.length == 3) {
return addTable(parsedName[0], parsedName[1], parsedName[2]);
}
else if (parsedName.length == 2) {
return addTable(parsedName[0], parsedName[1]);
}
else {
return addTable(parsedName[0]);
}
}
private ELTable addTable(ELTable table) {
this.addItemToCollection(table, this.tables, TABLES_COLLECTION);
return table;
}
public void removeTable(ELTable table) {
this.removeNodeFromCollection(table, this.tables, TABLES_COLLECTION);
}
public boolean containsTableNamed(String catalog, String schema, String shortName) {
return this.tableNamed(catalog, schema, shortName) != null;
}
public ELTable tableNamed(String catalog, String schema, String shortName) {
synchronized (this.tables) {
for (ELTable table : this.tables) {
if (table.nameMatches(catalog, schema, shortName)) {
return table;
}
}
}
return null;
}
public boolean containsTableNamedIgnoreCase(String catalog, String schema, String shortName) {
return this.tableNamedIgnoreCase(catalog, schema, shortName) != null;
}
public ELTable tableNamedIgnoreCase(String catalog, String schema, String shortName) {
synchronized (this.tables) {
for (ELTable table : this.tables) {
if (table.nameMatchesIgnoreCase(catalog, schema, shortName)) {
return table;
}
}
}
return null;
}
public boolean containsTableNamed(String qualifiedName) {
return this.tableNamed(qualifiedName) != null;
}
public ELTable tableNamed(String qualifiedName) {
synchronized (this.tables) {
for (ELTable table : this.tables) {
if (table.qualifiedName().equals(qualifiedName)) {
return table;
}
}
}
return null;
}
/**
* used to prevent adding a duplicate table
*/
public Iterator<String> tableNames() {
return new TransformationIterator<ELTable, String>(this.tables()) {
@Override
protected String transform(ELTable next) {
return next.getName();
}
};
}
public ELColumn columnNamed(String qualifiedName) {
ELTable table = this.tableNamed(ELColumn.parseTableNameFromQualifiedName(qualifiedName));
if (table == null) {
return null;
}
return table.columnNamed(ELColumn.parseColumnNameFromQualifiedName(qualifiedName));
}
// ********** external database **********
/**
* PRIVATE - no one should need direct access to the external database
*/
public ExternalDatabase getExternalDatabase() {
if (this.externalDatabase == null) {
this.externalDatabase = this.buildExternalDatabase();
}
return this.externalDatabase;
}
private ExternalDatabase buildExternalDatabase() {
// when executing under jdev, the connection will be null
return this.externalDatabaseFactory().buildDatabase(this.connection);
}
private ExternalDatabaseFactory getExternalDatabaseFactory() {
if (this.dbFactory == null) {
this.dbFactory = new JDBCExternalDatabaseFactory();
}
return this.dbFactory;
}
// ********** connection **********
/**
* this method is not called when executing under jdev
*/
public boolean isConnected() {
return this.connection != null;
}
// ********** schema manager **********
/**
* call #isConnected()/#login() before calling this method or you
* might get an IllegalStateException;
* this method is not called when executing under jdev
*/
private SchemaManager getSchemaManager() {
if (this.schemaManager == null) {
throw new IllegalStateException("not connected");
}
return this.schemaManager;
}
// ********** queries **********
/**
* the external database factory is supplied by client code
*/
private ExternalDatabaseFactory externalDatabaseFactory() {
return getExternalDatabaseFactory();
}
boolean supportsIdentityClause() {
return this.databasePlatform.supportsIdentityClause();
}
// ********** miscellaneous behavior **********
/**
* 'connected' is a virtual property
*/
@Override
protected void addTransientAspectNamesTo(Set<String> transientAspectNames) {
super.addTransientAspectNamesTo(transientAspectNames);
transientAspectNames.add(CONNECTED_PROPERTY);
}
/**
* disallow duplicate login info names
*/
void checkLoginSpecName(String loginSpecName) {
if ((loginSpecName == null) || (loginSpecName.length() == 0)) {
throw new IllegalArgumentException();
}
if (this.containsLoginSpecNamed(loginSpecName)) {
throw new IllegalArgumentException("duplicate login spec name: " + loginSpecName);
}
}
/**
* disallow duplicate table names
*/
void checkTableName(String catalog, String schema, String shortName, ELTable table) {
this.checkTableNameQualifier(catalog);
this.checkTableNameQualifier(schema);
if ((shortName == null) || (shortName.length() == 0)) {
throw new IllegalArgumentException();
}
ELTable match = this.tableNamed(catalog, schema, shortName);
if (match != null) {
throw new IllegalArgumentException("duplicate table name: " + match.qualifiedName());
}
ELTable matchIgnoreCase = this.tableNamedIgnoreCase(catalog, schema, shortName);
if ((matchIgnoreCase != null) && (matchIgnoreCase != table)) {
throw new IllegalArgumentException("duplicate table name: " + matchIgnoreCase.qualifiedName());
}
}
/**
* table qualifiers must be null or non-empty
*/
private void checkTableNameQualifier(String qualifier) {
if (qualifier == null) {
return;
}
if (qualifier.length() == 0) {
throw new IllegalArgumentException();
}
}
// ********** model synchronization **********
@Override
protected void addChildrenTo(List<Node> children) {
super.addChildrenTo(children);
synchronized (this.loginSpecs) { children.addAll(this.loginSpecs); }
children.add(this.deploymentLoginSpecHandle);
children.add(this.developmentLoginSpecHandle);
synchronized (this.tables) { children.addAll(this.tables); }
}
private void loginSpecRemoved(ELLoginSpec loginSpec) {
if (this.getDeploymentLoginSpec() == loginSpec) {
this.setDeploymentLoginSpec(null);
}
if (this.getDevelopmentLoginSpec() == loginSpec) {
this.setDevelopmentLoginSpec(null);
}
}
/**
* performance tuning: override this method and assume
* the database's descendants have NO references (handles)
* to any models other than other descendants of the database
*/
@Override
public void nodeRemoved(Node node) {
if (node.isDescendantOf(this)) {
super.nodeRemoved(node);
}
}
/**
* performance tuning: override this method and assume
* the database's descendants have NO references (handles)
* to any models other than other descendants of the database
*/
@Override
public void nodeRenamed(Node node) {
if (node.isDescendantOf(this)) {
super.nodeRenamed(node);
// we handle a renamed table directly in #tableRenamed()
}
}
// ********** login/logout **********
/**
* you must log in before accessing the connection or
* schema manager;
* we instantiate and connect the JDBC Driver manually, ignoring
* the stupid JDBC DriverManager;
* this method is not called when executing under jdev
*/
public void login() throws SQLException, ClassNotFoundException {
if (this.isConnected()) {
throw new IllegalStateException("already connected");
}
ELLoginSpec loginSpec = this.getDevelopmentLoginSpec();
if (loginSpec == null) {
throw new IllegalStateException("missing development login spec");
}
try {
this.driver = loginSpec.buildDriver();
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
}
String url = loginSpec.getURL();
if ((url == null) || (url.length() == 0)) {
throw new IllegalStateException("missing database URL");
}
// store the user name and password in a dictionary
Properties props = new Properties();
String userName = loginSpec.getUserName();
if (userName != null) {
props.put("user", userName);
}
String password = loginSpec.getPassword();
if (password != null) {
props.put("password", password);
}
this.connection = this.driver.connect(url, props);
// once we are connected we can build the schema manager
this.schemaManager = this.buildSchemaManager();
this.firePropertyChanged(CONNECTED_PROPERTY, false, true);
}
/**
* disconnect from the database and clear out any state that relies
* on the connection;
* this method is not called when executing under jdev
*/
public void logout() throws SQLException {
if ( ! this.isConnected()) {
throw new IllegalStateException("not connected");
}
this.connection.close();
this.schemaManager = null;
this.connection = null;
this.driver = null;
this.externalDatabase = null;
this.firePropertyChanged(CONNECTED_PROPERTY, true, false);
}
private SchemaManager buildSchemaManager() {
return new SchemaManager(this.buildRuntimeDatabaseSession());
}
/**
* this db session will use the *development* login spec;
* this method is not called when executing under jdev
*/
private DatabaseSession buildRuntimeDatabaseSession() {
DatabaseSession session = this.buildRuntimeProject().createDatabaseSession();
session.dontLogMessages();
session.login();
return session;
}
/**
* this project will use the *development* login spec
* this method is not called when executing under jdev
*/
private Project buildRuntimeProject() {
return new Project(this.getDevelopmentLoginSpec().buildDevelopmentRuntimeDatabaseLogin());
}
/**
* build a connector that will use the database's connection;
* this method is not called when executing under jdev
*/
Connector buildRuntimeConnector() {
return new LocalConnectorAdapter(this.connection);
}
// ********** table importing/refreshing **********
/**
* Returns the "catalog" names from the current database.
* @see java.sql.DatabaseMetaData#getCatalogs()
*/
public Iterator<String> catalogNames() {
return new ArrayIterator<String>(this.getExternalDatabase().getCatalogNames());
}
/**
* Returns the "schema" names from the current database.
* @see java.sql.DatabaseMetaData#getSchemas()
*/
public Iterator<String> schemaNames() {
if (!getDatabasePlatform().getName().equals("MySQL")) {
return new ArrayIterator<String>(this.getExternalDatabase().getSchemaNames());
} else {
return new ArrayIterator<String>(this.getExternalDatabase().getCatalogNames());
}
}
/**
* Returns the "table type" names from the current database.
* @see java.sql.DatabaseMetaData#getTableTypes()
*/
public Iterator<String> tableTypeNames() {
return new ArrayIterator<String>(this.getExternalDatabase().getTableTypeNames());
}
/**
* Returns the "external" table descriptions corresponding to the specified
* search criteria; these can then be used to import and/or refresh tables
* @see #externalTableDescriptions()
* @see #importQualifiedTablesFor(java.util.Collection)
* @see #importUnqualifiedTablesFor(java.util.Collection)
* @see #refreshQualifiedTablesFor(java.util.Collection)
* @see java.sql.DatabaseMetaData#getTables(String, String, String, String[])
*/
public Iterator<ExternalTableDescription> externalTableDescriptions(String catalog, String schemaPattern, String tableNamePattern, String[] types) {
return new ArrayIterator<ExternalTableDescription>(
this.getExternalDatabase().getTableDescriptions(catalog, schemaPattern, tableNamePattern, types)
);
}
/**
* Returns the all the "external" table descriptions;
* these can then be used to import and/or refresh tables
* @see #externalTableDescriptions(String, String, String, String[])
* @see #importQualifiedTablesFor(java.util.Collection)
* @see #importUnqualifiedTablesFor(java.util.Collection)
* @see #refreshQualifiedTablesFor(java.util.Collection)
*/
public Iterator<ExternalTableDescription> externalTableDescriptions() {
return new ArrayIterator<ExternalTableDescription>(
this.getExternalDatabase().getTableDescriptions()
);
}
/**
* import the tables corresponding to the specified
* "external" table descriptions, using their fully-qualified names;
* this is a two-step process: all the fields must be in place before
* we can build or refresh the references
*/
public void importQualifiedTablesFor(Collection<ExternalTableDescription> externalTableDescriptions) {
for (Iterator<ExternalTableDescription> stream = externalTableDescriptions.iterator(); stream.hasNext(); ) {
ExternalTableDescription externalTableDescription = stream.next();
this.qualifiedTableFor(externalTableDescription).refreshColumns(externalTableDescription.getTable());
}
for (Iterator<ExternalTableDescription> stream = externalTableDescriptions.iterator(); stream.hasNext(); ) {
ExternalTableDescription externalTableDescription = stream.next();
this.qualifiedTableFor(externalTableDescription).refreshReferences(externalTableDescription.getTable());
}
}
/**
* refresh the tables corresponding to the specified "external" table descriptions, using their fully
* qualified names; this is a two-step process: all the fields must be in place before
* we can refresh the references;
* we want different behavior here because we don't want to
* create them if they don't exist and we must handle unqualified tables
*/
public void refreshQualifiedTablesFor(Collection<ExternalTableDescription> externalTableDescriptions) {
for (Iterator<ExternalTableDescription> stream = externalTableDescriptions.iterator(); stream.hasNext(); ) {
ExternalTableDescription externalTableDescription = stream.next();
ELTable tableToRefresh = this.tableNamed(externalTableDescription.getQualifiedName());
if (tableToRefresh == null) {
// the table's name may be unqualified
tableToRefresh = this.tableNamed(externalTableDescription.getName());
}
if (tableToRefresh != null) {
tableToRefresh.refreshColumns(externalTableDescription.getTable());
}
}
for (Iterator<ExternalTableDescription> stream = externalTableDescriptions.iterator(); stream.hasNext(); ) {
ExternalTableDescription externalTableDescription = stream.next();
ELTable tableToRefresh = this.tableNamed(externalTableDescription.getQualifiedName());
if (tableToRefresh == null) {
// the table's name may be unqualified
tableToRefresh = this.tableNamed(externalTableDescription.getName());
}
if (tableToRefresh != null) {
tableToRefresh.refreshReferences(externalTableDescription.getTable());
}
}
}
/**
* Returns the table corresponding to the specified "external" table description,
* creating it if necessary; use the table's fully-qualified name
*/
private ELTable qualifiedTableFor(ExternalTableDescription externalTableDescription) {
if (getDatabasePlatform().getName().equals("MySQL")) {
return this.tableNamedForImport(null, externalTableDescription.getCatalogName(), externalTableDescription.getName());
} else {
return this.tableNamedForImport(externalTableDescription.getCatalogName(), externalTableDescription.getSchemaName(), externalTableDescription.getName());
}
}
/**
* Returns the requested table, creating it if necessary
*/
private ELTable tableNamedForImport(String catalog, String schema, String shortName) {
ELTable table = this.tableNamed(catalog, schema, shortName);
if (table == null) {
table = this.addTable(catalog, schema, shortName);
}
return table;
}
/**
* import and/or refresh the tables corresponding to the specified
* "external" table descriptions, using their "short" names;
* this is a two-step process: all the fields must be in place before
* we can build or refresh the references
*/
public void importUnqualifiedTablesFor(Collection<ExternalTableDescription> externalTableDescriptions) {
for (Iterator<ExternalTableDescription> stream = externalTableDescriptions.iterator(); stream.hasNext(); ) {
ExternalTableDescription externalTableDescription = stream.next();
this.unqualifiedTableFor(externalTableDescription).refreshColumns(externalTableDescription.getTable());
}
for (Iterator<ExternalTableDescription> stream = externalTableDescriptions.iterator(); stream.hasNext(); ) {
ExternalTableDescription externalTableDescription = stream.next();
this.unqualifiedTableFor(externalTableDescription).refreshReferences(externalTableDescription.getTable());
}
}
/**
* Returns the table corresponding to the specified "external" table description,
* creating it if necessary; use the table's "short" name
*/
private ELTable unqualifiedTableFor(ExternalTableDescription externalTableDescription) {
return this.tableNamedForImport(null, null, externalTableDescription.getName());
}
// ********** printing and displaying **********
public void toString(StringBuffer sb) {
sb.append(this.getDatabasePlatform().getName());
sb.append(" : ");
sb.append(this.tables.size());
sb.append(" tables");
}
@Override
public String displayString() {
StringBuffer sb = new StringBuffer();
sb.append("Database (");
sb.append(this.getDatabasePlatform().getName());
sb.append(")");
return sb.toString();
}
// ********** SubComponentContainer implementation **********
public Iterable projectSubFileComponents() {
return this.tables();
}
public void setProjectSubFileComponents(Collection subComponents) {
this.tables = subComponents;
}
public Iterator originalProjectSubFileComponentNames() {
return this.tableNames.iterator();
}
public void setOriginalProjectSubFileComponentNames(Collection originalSubComponentNames) {
this.tableNames = originalSubComponentNames;
}
public boolean hasChangedMainProjectSaveFile() {
if (this.isDirty()) {
// the database itself is dirty
return true;
}
for (Iterator stream = this.children(); stream.hasNext(); ) {
if (this.childHasChangedTheProjectSaveFile(stream.next())) {
return true;
}
}
// the tables might be dirty
return false;
}
/**
* Returns whether the specified child of the database is dirty AND
* is written to the .mwp file
*/
private boolean childHasChangedTheProjectSaveFile(Object child) {
if (this.tables.contains(child)) {
// tables are written to separate files
return false;
}
// the child is NOT a table,
// so all of its state is written to the .mwp file
return ((Node) child).isDirtyBranch();
}
// ********** inner classes **********
/**
* Adapt the database to the TopLink run-time Connector interface.
*/
private static class LocalConnectorAdapter implements Connector {
private Connection connection;
LocalConnectorAdapter(Connection connection) {
super();
this.connection = connection;
}
/** this is the only method of note */
@Override
public Connection connect(Properties properties, Session session) {
return this.connection;
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException ex) {
throw new InternalError();
}
}
@Override
public String getConnectionDetails() {
return "MWDatabase.LocalConnectorAdapter";
}
@Override
public void toString(PrintWriter writer) {
writer.print(this.getConnectionDetails());
}
}
}