| /******************************************************************************* |
| * 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()); |
| } |
| } |
| } |