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