Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 7821854ecfc7101834367c20fe27d6bee67e5ecb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
/*******************************************************************************
 * 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 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.ArrayTools;
import org.eclipse.jpt.utility.internal.CollectionTools;
import org.eclipse.jpt.utility.internal.iterables.ArrayIterable;
import org.eclipse.jpt.utility.internal.iterables.TransformationIterable;

/**
 * Wrap a DTP Database.
 * <p>
 * Catalogs vs. Schemata:<br>
 * 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.
 * <p>
 * Catalog Note 1:<br>
 * 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.
 * <p>
 * Catalog Note 2:<br>
 * 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....
 * <p>
 * (Yet Another) Note:<br>
 * 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(Iterable<T> databaseObjects, String identifier) {
		return super.selectDatabaseObjectForIdentifier(databaseObjects, identifier);
	}

	// ***** catalogs

	public boolean supportsCatalogs() {
		return this.vendor.supportsCatalogs(this.dtpDatabase);
	}

	public Iterable<Catalog> getCatalogs() {
		return new ArrayIterable<Catalog>(this.getCatalogArray());
	}

	private Iterable<DTPCatalogWrapper> getCatalogWrappers() {
		return new ArrayIterable<DTPCatalogWrapper>(this.getCatalogArray());
	}

	private synchronized DTPCatalogWrapper[] getCatalogArray() {
		if (this.catalogs == null) {
			this.catalogs = this.buildCatalogArray();
		}
		return this.catalogs;
	}

	private DTPCatalogWrapper[] buildCatalogArray() {
		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 ArrayTools.sort(result, DEFAULT_COMPARATOR);
	}

	private List<org.eclipse.datatools.modelbase.sql.schema.Catalog> getDTPCatalogs() {
		return this.vendor.getCatalogs(this.dtpDatabase);
	}

	public int getCatalogsSize() {
		return this.getCatalogArray().length;
	}

	/**
	 * return the catalog for the specified DTP catalog
	 */
	DTPCatalogWrapper getCatalog(org.eclipse.datatools.modelbase.sql.schema.Catalog dtpCatalog) {
		for (DTPCatalogWrapper catalog : this.getCatalogArray()) {
			if (catalog.wraps(dtpCatalog)) {
				return catalog;
			}
		}
		throw new IllegalArgumentException("invalid DTP catalog: " + dtpCatalog);  //$NON-NLS-1$
	}

	public Iterable<String> getSortedCatalogNames() {
		// the catalogs are already sorted
		return new TransformationIterable<DatabaseObject, String>(this.getCatalogWrappers(), NAME_TRANSFORMER);
	}

	public DTPCatalogWrapper getCatalogNamed(String name) {
		return this.selectDatabaseObjectNamed(this.getCatalogWrappers(), name);
	}

	public Iterable<String> getSortedCatalogIdentifiers() {
		// the catalogs are already sorted
		return new TransformationIterable<DatabaseObject, String>(this.getCatalogWrappers(), IDENTIFIER_TRANSFORMER);
	}

	public DTPCatalogWrapper getCatalogForIdentifier(String identifier) {
		return this.selectDatabaseObjectForIdentifier(this.getCatalogWrappers(), identifier);
	}

	public synchronized DTPCatalogWrapper getDefaultCatalog() {
		return this.getCatalogForNames(this.getDefaultCatalogNames());
	}

	private Iterable<String> getDefaultCatalogNames() {
		return this.vendor.getDefaultCatalogNames(this.dtpDatabase, this.getConnectionProfile().getUserName());
	}

	/**
	 * Return the first catalog found.
	 */
	private DTPCatalogWrapper getCatalogForNames(Iterable<String> names) {
		for (String name : names) {
			DTPCatalogWrapper catalog = this.getCatalogNamed(name);
			if (catalog != null) {
				return catalog;
			}
		}
		return null;
	}

	/**
	 * If we find a default catalog, return its identifier;
	 * otherwise, return the last name on the list of default names.
	 * (Some databases have multiple possible default names.)
	 * Return null if the database does not support catalogs.
	 */
	public synchronized String getDefaultCatalogIdentifier() {
		Iterable<String> names = this.getDefaultCatalogNames();
		DTPCatalogWrapper catalog = this.getCatalogForNames(names);
		if (catalog != null) {
			return catalog.getIdentifier();
		}
		Iterator<String> stream = names.iterator();
		return stream.hasNext() ? this.convertNameToIdentifier(CollectionTools.last(stream)) : null;
	}

	// ***** schemata

	Iterable<String> getDefaultSchemaNames() {
		return this.vendor.getDefaultSchemaNames(this.dtpDatabase, this.getConnectionProfile().getUserName());
	}


	// ********** names vs. identifiers **********

	// override to make method public since it's in the Database interface
	@Override
	public String convertNameToIdentifier(String name) {
		return super.convertNameToIdentifier(name);
	}

	/**
	 * Delegate to the vendor.
	 */
	String convertNameToIdentifier_(String name) {
		return this.vendor.convertNameToIdentifier(name);
	}

	/**
	 * Delegate to the vendor.
	 */
	String convertIdentifierToName_(String identifier) {
		return this.vendor.convertIdentifierToName(identifier);
	}

	/**
	 * Convert the specified name to an identifier. Return null if the resulting
	 * identifier matches the specified default name.
	 * <p>
	 * This is used by the old entity generation code to determine whether
	 * a generated annotation must explicitly identify a database object
	 * (e.g. a table) or the specified default adequately identifies the
	 * specified database object (taking into consideration case-sensitivity
	 * and special characters).
	 */
	// TODO add to database identifier adapter? currently not used...
	String convertNameToIdentifier(String name, String defaultName) {
		return this.vendor.convertNameToIdentifier(name, defaultName);
	}


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

}

Back to the top