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 package com.workingdogs.village;
54
55 import java.io.ByteArrayOutputStream;
56 import java.io.PrintWriter;
57
58 import java.sql.Connection;
59 import java.sql.ResultSet;
60 import java.sql.ResultSetMetaData;
61 import java.sql.SQLException;
62 import java.sql.Statement;
63
64 import java.util.Enumeration;
65 import java.util.Hashtable;
66
67 /***
68 * The Schema object represents the <a href="Column.html">Columns</a> in a database table. It contains a collection of <a
69 * href="Column.html">Column</a> objects.
70 *
71 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
72 * @author John D. McNally
73 * @version $Revision: 568 $
74 */
75 public final class Schema
76 {
77 /*** TODO: DOCUMENT ME! */
78 private String tableName;
79
80 /*** TODO: DOCUMENT ME! */
81 private String columnsAttribute;
82
83 /*** TODO: DOCUMENT ME! */
84 private int numberOfColumns;
85
86 /*** TODO: DOCUMENT ME! */
87 private Column [] columns;
88
89 /*** TODO: DOCUMENT ME! */
90 private static Hashtable schemaCache = new Hashtable();
91
92 /***
93 * This attribute is used to complement columns in the event that this schema represents more than one table. Its keys are
94 * String contains table names and its elements are Hashtables containing columns.
95 */
96 private Hashtable tableHash = null;
97
98 /*** TODO: DOCUMENT ME! */
99 private boolean singleTable = true;
100
101 /***
102 * A blank Schema object
103 */
104 public Schema()
105 {
106 this.tableName = "";
107 this.columnsAttribute = null;
108 this.numberOfColumns = 0;
109 }
110
111 /***
112 * Creates a Schema with all columns
113 *
114 * @param conn
115 * @param tableName
116 *
117 * @return an instance of myself
118 *
119 * @exception SQLException
120 * @exception DataSetException
121 */
122 public Schema schema(Connection conn, String tableName)
123 throws SQLException, DataSetException
124 {
125 return schema(conn, tableName, "*");
126 }
127
128 /***
129 * Creates a Schema with the named columns in the columnsAttribute
130 *
131 * @param conn
132 * @param tableName
133 * @param columnsAttribute
134 *
135 * @return an instance of myself
136 *
137 * @exception SQLException
138 * @exception DataSetException
139 */
140 public synchronized Schema schema(Connection conn, String tableName, String columnsAttribute)
141 throws SQLException, DataSetException
142 {
143 if (columnsAttribute == null)
144 {
145 columnsAttribute = "*";
146 }
147
148 Statement stmt = null;
149
150 try
151 {
152 String keyValue = conn.getMetaData().getURL() + tableName;
153 Schema tableSchema = (Schema) schemaCache.get(keyValue);
154
155 if (tableSchema == null)
156 {
157 String sql = "SELECT " + columnsAttribute + " FROM " + tableName + " WHERE 1 = -1";
158 stmt = conn.createStatement();
159
160 ResultSet rs = stmt.executeQuery(sql);
161
162 if (rs != null)
163 {
164 tableSchema = new Schema();
165 tableSchema.setTableName(tableName);
166 tableSchema.setAttributes(columnsAttribute);
167 tableSchema.populate(rs.getMetaData(), tableName);
168 schemaCache.put(keyValue, tableSchema);
169 }
170 else
171 {
172 throw new DataSetException("Couldn't retrieve schema for " + tableName);
173 }
174 }
175
176 return tableSchema;
177 }
178 finally
179 {
180 if (stmt != null)
181 {
182 stmt.close();
183 }
184 }
185 }
186
187 /***
188 * Appends data to the tableName that this schema was first created with.
189 *
190 * <P></p>
191 *
192 * @param app String to append to tableName
193 *
194 * @see TableDataSet#tableQualifier(java.lang.String)
195 */
196 void appendTableName(String app)
197 {
198 this.tableName = this.tableName + " " + app;
199 }
200
201 /***
202 * List of columns to select from the table
203 *
204 * @return the list of columns to select from the table
205 */
206 public String attributes()
207 {
208 return this.columnsAttribute;
209 }
210
211 /***
212 * Returns the requested Column object at index i
213 *
214 * @param i
215 *
216 * @return the requested column
217 *
218 * @exception DataSetException
219 */
220 public Column column(int i)
221 throws DataSetException
222 {
223 if (i == 0)
224 {
225 throw new DataSetException("Columns are 1 based");
226 }
227 else if (i > numberOfColumns)
228 {
229 throw new DataSetException("There are only " + numberOfColumns() + " available!");
230 }
231
232 try
233 {
234 return columns[i];
235 }
236 catch (Exception e)
237 {
238 throw new DataSetException("Column number: " + numberOfColumns() + " does not exist!");
239 }
240 }
241
242 /***
243 * Returns the requested Column object by name
244 *
245 * @param colName
246 *
247 * @return the requested column
248 *
249 * @exception DataSetException
250 */
251 public Column column(String colName)
252 throws DataSetException
253 {
254 return column(index(colName));
255 }
256
257 /***
258 * Returns the requested Column object by name
259 *
260 * @param colName
261 *
262 * @return the requested column
263 *
264 * @exception DataSetException
265 */
266 public Column getColumn(String colName)
267 throws DataSetException
268 {
269 int dot = colName.indexOf('.');
270
271 if (dot > 0)
272 {
273 String table = colName.substring(0, dot);
274 String col = colName.substring(dot + 1);
275
276 return getColumn(table, col);
277 }
278
279 return column(index(colName));
280 }
281
282 /***
283 * Returns the requested Column object belonging to the specified table by name
284 *
285 * @param tableName
286 * @param colName
287 *
288 * @return the requested column, null if a column by the specified name does not exist.
289 *
290 * @exception DataSetException
291 */
292 public Column getColumn(String tableName, String colName)
293 throws DataSetException
294 {
295 return (Column) ((Hashtable) tableHash.get(tableName)).get(colName);
296 }
297
298 /***
299 * Returns an array of columns
300 *
301 * @return an array of columns
302 */
303 Column [] getColumns()
304 {
305 return this.columns;
306 }
307
308 /***
309 * returns the table name that this Schema represents
310 *
311 * @return the table name that this Schema represents
312 *
313 * @throws DataSetException TODO: DOCUMENT ME!
314 */
315 public String getTableName()
316 throws DataSetException
317 {
318 if (singleTable)
319 {
320 return tableName;
321 }
322 else
323 {
324 throw new DataSetException("This schema represents several tables.");
325 }
326 }
327
328 /***
329 * returns all table names that this Schema represents
330 *
331 * @return the table names that this Schema represents
332 */
333 public String [] getAllTableNames()
334 {
335 Enumeration e = tableHash.keys();
336 String [] tableNames = new String[tableHash.size()];
337
338 for (int i = 0; e.hasMoreElements(); i++)
339 {
340 tableNames[i] = (String) e.nextElement();
341 }
342
343 return tableNames;
344 }
345
346 /***
347 * Gets the index position of a named column. If multiple tables are represented and they have columns with the same name,
348 * this method returns the first one listed, if the table name is not specified.
349 *
350 * @param colName
351 *
352 * @return the requested column index integer
353 *
354 * @exception DataSetException
355 */
356 public int index(String colName)
357 throws DataSetException
358 {
359 int dot = colName.indexOf('.');
360
361 if (dot > 0)
362 {
363 String table = colName.substring(0, dot);
364 String col = colName.substring(dot + 1);
365
366 return index(table, col);
367 }
368
369 for (int i = 1; i <= numberOfColumns(); i++)
370 {
371 if (columns[i].name().equalsIgnoreCase(colName))
372 {
373 return i;
374 }
375 }
376
377 throw new DataSetException("Column name: " + colName + " does not exist!");
378 }
379
380 /***
381 * Gets the index position of a named column.
382 *
383 * @param tableName
384 * @param colName
385 *
386 * @return the requested column index integer
387 *
388 * @exception DataSetException
389 */
390 public int index(String tableName, String colName)
391 throws DataSetException
392 {
393 for (int i = 1; i <= numberOfColumns(); i++)
394 {
395 if (columns[i].name().equalsIgnoreCase(colName) && columns[i].getTableName().equalsIgnoreCase(tableName))
396 {
397 return i;
398 }
399 }
400
401 throw new DataSetException("Column name: " + colName + " does not exist!");
402 }
403
404 /***
405 * Checks to see if this DataSet represents one table in the database.
406 *
407 * @return true if only one table is represented, false otherwise.
408 */
409 public boolean isSingleTable()
410 {
411 return singleTable;
412 }
413
414 /***
415 * Gets the number of columns in this Schema
416 *
417 * @return integer number of columns
418 */
419 public int numberOfColumns()
420 {
421 return this.numberOfColumns;
422 }
423
424 /***
425 * Internal method which populates this Schema object with Columns.
426 *
427 * @param meta The meta data of the ResultSet used to build this Schema.
428 * @param tableName The name of the table referenced in this schema, or null if unknown or multiple tables are involved.
429 *
430 * @exception SQLException
431 * @exception DataSetException
432 */
433 void populate(ResultSetMetaData meta, String tableName)
434 throws SQLException, DataSetException
435 {
436 this.numberOfColumns = meta.getColumnCount();
437 columns = new Column[numberOfColumns() + 1];
438
439 for (int i = 1; i <= numberOfColumns(); i++)
440 {
441 Column col = new Column();
442 col.populate(meta, i, tableName);
443 columns[i] = col;
444
445 if ((i > 1) && !col.getTableName().equalsIgnoreCase(columns[i - 1].getTableName()))
446 {
447 singleTable = false;
448 }
449 }
450
451
452
453
454 if (singleTable)
455 {
456
457 if ((tableName != null) && (tableName.length() > 0))
458 {
459 setTableName(tableName);
460 }
461 else
462 {
463
464
465
466 try
467 {
468 setTableName(meta.getTableName(1));
469 }
470 catch (Exception e)
471 {
472 setTableName("");
473 }
474 }
475 }
476 else
477 {
478 tableHash = new Hashtable((int) ((1.25 * numberOfColumns) + 1));
479
480 for (int i = 1; i <= numberOfColumns(); i++)
481 {
482 if (tableHash.containsKey(columns[i].getTableName()))
483 {
484 ((Hashtable) tableHash.get(columns[i].getTableName())).put(columns[i].name(), columns[i]);
485 }
486 else
487 {
488 Hashtable columnHash = new Hashtable((int) ((1.25 * numberOfColumns) + 1));
489 columnHash.put(columns[i].name(), columns[i]);
490 tableHash.put(columns[i].getTableName(), columnHash);
491 }
492 }
493 }
494 }
495
496 /***
497 * Sets the columns to select from the table
498 *
499 * @param attributes comma separated list of column names
500 */
501 void setAttributes(String attributes)
502 {
503 this.columnsAttribute = attributes;
504 }
505
506 /***
507 * Sets the table name that this Schema represents
508 *
509 * @param tableName
510 */
511 void setTableName(String tableName)
512 {
513 this.tableName = tableName;
514 }
515
516 /***
517 * returns the table name that this Schema represents
518 *
519 * @return the table name that this Schema represents
520 *
521 * @throws DataSetException TODO: DOCUMENT ME!
522 */
523 public String tableName()
524 throws DataSetException
525 {
526 return getTableName();
527 }
528
529 /***
530 * This returns a representation of this Schema
531 *
532 * @return a string
533 */
534 public String toString()
535 {
536 ByteArrayOutputStream bout = new ByteArrayOutputStream();
537 PrintWriter out = new PrintWriter(bout);
538 out.print('{');
539
540 for (int i = 1; i <= numberOfColumns; i++)
541 {
542 out.print('\'');
543
544 if (!singleTable)
545 {
546 out.print(columns[i].getTableName() + '.');
547 }
548
549 out.print(columns[i].name() + '\'');
550
551 if (i < numberOfColumns)
552 {
553 out.print(',');
554 }
555 }
556
557 out.print('}');
558 out.flush();
559
560 return bout.toString();
561 }
562 }