blob: 529d8eec0d98b3c1af94eb6284551826a4e69675 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2009 Oracle. 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:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.db.internal;
import com.ibm.icu.text.Collator;
import java.util.Iterator;
import java.util.List;
import org.eclipse.datatools.connectivity.sqm.core.definition.DatabaseDefinition;
import org.eclipse.datatools.connectivity.sqm.internal.core.RDBCorePlugin;
import org.eclipse.jpt.db.Catalog;
import org.eclipse.jpt.db.Database;
import org.eclipse.jpt.db.DatabaseObject;
import org.eclipse.jpt.db.internal.vendor.Vendor;
import org.eclipse.jpt.db.internal.vendor.VendorRepository;
import org.eclipse.jpt.utility.internal.CollectionTools;
import org.eclipse.jpt.utility.internal.iterators.ArrayIterator;
import org.eclipse.jpt.utility.internal.iterators.TransformationIterator;
/**
* Wrap a DTP Database.
*
* Catalogs vs. Schemata:
* Typically, if a DTP database does not support "catalogs",
* o.e.datatools.modelbase.sql.schema.Database#getCatalogs() will return a
* single catalog without a name (actually, it's an empty string). This catalog
* will contain all the database's schemata. We try to ignore this catalog and
* return the schemata from the database directly.
*
* Catalog Note 1:
* As of Jan 2009, the DTP MySQL driver is not consistent with this pattern.
* A DTP MySQL database has *no* catalogs; it holds a single schema
* directly, and that schema has the same name as the database. See bug 249013.
*
* Catalog Note 2:
* As of Jan 2009, the PostgreSQL JDBC driver complicates this pattern a bit.
* Even though PostgreSQL does not support "catalogs", its JDBC driver
* returns a single catalog that has the same name as the database specified
* in the JDBC connection URL. The DTP PostgreSQL driver simply replicates this
* behavior. Unfortunately, this catalog can be unnamed; i.e. its name is an
* empty string....
*
* (Yet Another) Note:
* We use "name" when dealing with the unmodified name of a database object
* as supplied by the database itself (i.e. it is not delimited and it is always
* case-sensitive).
* We use "identifier" when dealing with a string representation of a database
* object name (i.e. it may be delimited and, depending on the vendor, it may
* be case-insensitive).
*/
final class DTPDatabaseWrapper
extends DTPSchemaContainerWrapper
implements Database
{
// the wrapped DTP database
private final org.eclipse.datatools.modelbase.sql.schema.Database dtpDatabase;
// vendor-specific behavior
private final Vendor vendor;
// lazy-initialized, sorted
private DTPCatalogWrapper[] catalogs;
// ********** constructor **********
DTPDatabaseWrapper(DTPConnectionProfileWrapper connectionProfile, org.eclipse.datatools.modelbase.sql.schema.Database dtpDatabase) {
super(connectionProfile, dtpDatabase);
this.dtpDatabase = dtpDatabase;
this.vendor = VendorRepository.instance().getVendor(this.getVendorName());
}
// ********** DTPWrapper implementation **********
/* TODO
* We might want to listen to the "virtual" catalog; but that's probably
* not necessary since there is no easy way for the user to refresh it
* (i.e. it is not displayed in the DTP UI).
*/
@Override
synchronized void catalogObjectChanged() {
super.catalogObjectChanged();
this.getConnectionProfile().databaseChanged(this);
}
@Override
public DTPDatabaseWrapper getDatabase() {
return this;
}
// ********** DTPSchemaContainerWrapper implementation **********
@Override
List<org.eclipse.datatools.modelbase.sql.schema.Schema> getDTPSchemata() {
return this.vendor.getSchemas(this.dtpDatabase);
}
@Override
DTPSchemaWrapper getSchema(org.eclipse.datatools.modelbase.sql.schema.Schema dtpSchema) {
return this.getSchema_(dtpSchema);
}
DTPSchemaWrapper getSchemaFromCatalogs(org.eclipse.datatools.modelbase.sql.schema.Schema dtpSchema) {
return this.getCatalog(dtpSchema.getCatalog()).getSchema_(dtpSchema);
}
/**
* this is only called from a schema when the database is the schema
* container, so we know we don't have any catalogs
*/
@Override
DTPTableWrapper getTable(org.eclipse.datatools.modelbase.sql.tables.Table dtpTable) {
return this.getTable_(dtpTable);
}
/**
* this is only called from a catalog, so we know we have catalogs;
* i.e. the search has to descend through catalogs, then to schemata
*/
DTPTableWrapper getTableFromCatalogs(org.eclipse.datatools.modelbase.sql.tables.Table dtpTable) {
return this.getCatalog(dtpTable.getSchema().getCatalog()).getTable_(dtpTable);
}
/**
* this is only called from a schema when the database is the schema
* container, so we know we don't have any catalogs
*/
@Override
DTPColumnWrapper getColumn(org.eclipse.datatools.modelbase.sql.tables.Column dtpColumn) {
return this.getColumn_(dtpColumn);
}
/**
* this is only called from a catalog, so we know we have catalogs;
* i.e. the search has to descend through catalogs, then to schemata
*/
DTPColumnWrapper getColumnFromCatalogs(org.eclipse.datatools.modelbase.sql.tables.Column dtpColumn) {
return this.getCatalog(dtpColumn.getTable().getSchema().getCatalog()).getColumn_(dtpColumn);
}
// ********** DatabaseObject implementation **********
public String getName() {
return this.dtpDatabase.getName();
}
// ********** Database implementation **********
public String getVendorName() {
return this.dtpDatabase.getVendor();
}
public String getVersion() {
return this.dtpDatabase.getVersion();
}
// override to make method public since it's in the Database interface
@Override
public <T extends DatabaseObject> T selectDatabaseObjectForIdentifier(T[] databaseObjects, String identifier) {
return super.selectDatabaseObjectForIdentifier(databaseObjects, identifier);
}
// ***** catalogs
public boolean supportsCatalogs() {
return this.vendor.supportsCatalogs(this.dtpDatabase);
}
public Iterator<Catalog> catalogs() {
return new ArrayIterator<Catalog>(this.getCatalogs());
}
private Iterator<DTPCatalogWrapper> catalogWrappers() {
return new ArrayIterator<DTPCatalogWrapper>(this.getCatalogs());
}
private synchronized DTPCatalogWrapper[] getCatalogs() {
if (this.catalogs == null) {
this.catalogs = this.buildCatalogs();
}
return this.catalogs;
}
private DTPCatalogWrapper[] buildCatalogs() {
List<org.eclipse.datatools.modelbase.sql.schema.Catalog> dtpCatalogs = this.getDTPCatalogs();
DTPCatalogWrapper[] result = new DTPCatalogWrapper[dtpCatalogs.size()];
for (int i = result.length; i-- > 0;) {
result[i] = new DTPCatalogWrapper(this, dtpCatalogs.get(i));
}
return CollectionTools.sort(result);
}
private List<org.eclipse.datatools.modelbase.sql.schema.Catalog> getDTPCatalogs() {
return this.vendor.getCatalogs(this.dtpDatabase);
}
public int catalogsSize() {
return this.getCatalogs().length;
}
/**
* return the catalog for the specified DTP catalog
*/
DTPCatalogWrapper getCatalog(org.eclipse.datatools.modelbase.sql.schema.Catalog dtpCatalog) {
for (DTPCatalogWrapper catalog : this.getCatalogs()) {
if (catalog.wraps(dtpCatalog)) {
return catalog;
}
}
throw new IllegalArgumentException("invalid DTP catalog: " + dtpCatalog); //$NON-NLS-1$
}
public DTPCatalogWrapper getCatalogNamed(String name) {
return this.selectDatabaseObjectNamed(this.getCatalogs(), name);
}
public Iterator<String> sortedCatalogIdentifiers() {
// the catalogs are already sorted
return new TransformationIterator<DTPCatalogWrapper, String>(this.catalogWrappers()) {
@Override
protected String transform(DTPCatalogWrapper next) {
return next.getIdentifier();
}
};
}
public DTPCatalogWrapper getCatalogForIdentifier(String identifier) {
return this.selectDatabaseObjectForIdentifier(this.getCatalogs(), identifier);
}
public synchronized DTPCatalogWrapper getDefaultCatalog() {
return this.getCatalogForIdentifiers(this.getDefaultCatalogIdentifiers());
}
private List<String> getDefaultCatalogIdentifiers() {
return this.vendor.getDefaultCatalogIdentifiers(this.dtpDatabase, this.getConnectionProfile().getUserName());
}
/**
* Return the first catalog found.
*/
private DTPCatalogWrapper getCatalogForIdentifiers(List<String> identifiers) {
for (String identifier : identifiers) {
DTPCatalogWrapper catalog = this.getCatalogForIdentifier(identifier);
if (catalog != null) {
return catalog;
}
}
return null;
}
// ***** schemata
List<String> getDefaultSchemaIdentifiers() {
return this.vendor.getDefaultSchemaIdentifiers(this.dtpDatabase, this.getConnectionProfile().getUserName());
}
// ********** names vs. identifiers **********
/**
* Convert the specified name to an identifier. Return null if the resulting
* identifier matches the specified default name.
*/
String convertNameToIdentifier(String name, String defaultName) {
return this.vendor.convertNameToIdentifier(name, defaultName);
}
/**
* Convert the specified name to an identifier.
*/
public String convertNameToIdentifier(String name) {
return this.vendor.convertNameToIdentifier(name);
}
/**
* Return the database object identified by the specified identifier. If
* the identifier is "delimited" (typically with double-quotes), it will be
* used without any folding. If the name is "normal" (i.e. not delimited),
* it will be folded to the appropriate case (typically uppercase).
*
* Since the database has the appropriate state to compare identifiers and
* names, the connection profile delegates to here when using the default
* "database finder".
*/
<T extends DatabaseObject> T selectDatabaseObjectForIdentifier_(T[] databaseObjects, String identifier) {
return this.selectDatabaseObjectNamed(databaseObjects, this.convertIdentifierToName(identifier));
}
/**
* Convert the specified identifier to a name.
*/
String convertIdentifierToName(String identifier) {
return this.vendor.convertIdentifierToName(identifier);
}
// ********** Comparable implementation **********
public int compareTo(Database database) {
return Collator.getInstance().compare(this.getName(), database.getName());
}
// ********** internal methods **********
DatabaseDefinition getDTPDefinition() {
return RDBCorePlugin.getDefault().getDatabaseDefinitionRegistry().getDefinition(this.dtpDatabase);
}
// ********** listening **********
@Override
synchronized void startListening() {
if (this.catalogs != null) {
this.startCatalogs();
}
super.startListening();
}
private void startCatalogs() {
for (DTPCatalogWrapper catalog : this.catalogs) {
catalog.startListening();
}
}
@Override
synchronized void stopListening() {
if (this.catalogs != null) {
this.stopCatalogs();
}
super.stopListening();
}
private void stopCatalogs() {
for (DTPCatalogWrapper catalog : this.catalogs) {
catalog.stopListening();
}
}
// ********** clear **********
@Override
synchronized void clear() {
if (this.catalogs != null) {
this.clearCatalogs();
}
super.clear();
}
private void clearCatalogs() {
this.stopCatalogs();
for (DTPCatalogWrapper catalog : this.catalogs) {
catalog.clear();
}
this.catalogs = null;
}
}