%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
com.workingdogs.village.Schema |
|
|
1 | package com.workingdogs.village; |
|
2 | ||
3 | /* |
|
4 | * Licensed to the Apache Software Foundation (ASF) under one |
|
5 | * or more contributor license agreements. See the NOTICE file |
|
6 | * distributed with this work for additional information |
|
7 | * regarding copyright ownership. The ASF licenses this file |
|
8 | * to you under the Apache License, Version 2.0 (the |
|
9 | * "License"); you may not use this file except in compliance |
|
10 | * with the License. You may obtain a copy of the License at |
|
11 | * |
|
12 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
13 | * |
|
14 | * Unless required by applicable law or agreed to in writing, |
|
15 | * software distributed under the License is distributed on an |
|
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
|
17 | * KIND, either express or implied. See the License for the |
|
18 | * specific language governing permissions and limitations |
|
19 | * under the License. |
|
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 | 0 | 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 | 0 | private Hashtable tableHash = null; |
64 | ||
65 | /** TODO: DOCUMENT ME! */ |
|
66 | 0 | private boolean singleTable = true; |
67 | ||
68 | /** |
|
69 | * A blank Schema object |
|
70 | */ |
|
71 | public Schema() |
|
72 | 0 | { |
73 | 0 | this.tableName = ""; |
74 | 0 | this.columnsAttribute = null; |
75 | 0 | this.numberOfColumns = 0; |
76 | 0 | } |
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 | 0 | 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 | 0 | if (columnsAttribute == null) |
111 | { |
|
112 | 0 | columnsAttribute = "*"; |
113 | } |
|
114 | ||
115 | 0 | Statement stmt = null; |
116 | ||
117 | try |
|
118 | { |
|
119 | 0 | String keyValue = conn.getMetaData().getURL() + tableName; |
120 | 0 | Schema tableSchema = (Schema) schemaCache.get(keyValue); |
121 | ||
122 | 0 | if (tableSchema == null) |
123 | { |
|
124 | 0 | String sql = "SELECT " + columnsAttribute + " FROM " + tableName + " WHERE 1 = -1"; |
125 | 0 | stmt = conn.createStatement(); |
126 | ||
127 | 0 | ResultSet rs = stmt.executeQuery(sql); |
128 | ||
129 | 0 | if (rs != null) |
130 | { |
|
131 | 0 | tableSchema = new Schema(); |
132 | 0 | tableSchema.setTableName(tableName); |
133 | 0 | tableSchema.setAttributes(columnsAttribute); |
134 | 0 | tableSchema.populate(rs.getMetaData(), tableName); |
135 | 0 | schemaCache.put(keyValue, tableSchema); |
136 | } |
|
137 | else |
|
138 | { |
|
139 | 0 | throw new DataSetException("Couldn't retrieve schema for " + tableName); |
140 | } |
|
141 | } |
|
142 | ||
143 | 0 | return tableSchema; |
144 | } |
|
145 | finally |
|
146 | { |
|
147 | 0 | if (stmt != null) |
148 | { |
|
149 | 0 | 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 | 0 | this.tableName = class="keyword">this.tableName + " " + app; |
166 | 0 | } |
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 | 0 | 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 | 0 | if (i == 0) |
191 | { |
|
192 | 0 | throw new DataSetException("Columns are 1 based"); |
193 | } |
|
194 | 0 | else if (i > numberOfColumns) |
195 | { |
|
196 | 0 | throw new DataSetException("There are only " + numberOfColumns() + " available!"); |
197 | } |
|
198 | ||
199 | try |
|
200 | { |
|
201 | 0 | return columns[i]; |
202 | } |
|
203 | 0 | catch (Exception e) |
204 | { |
|
205 | 0 | 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 | 0 | 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 | 0 | int dot = colName.indexOf('.'); |
237 | ||
238 | 0 | if (dot > 0) |
239 | { |
|
240 | 0 | String table = colName.substring(0, dot); |
241 | 0 | String col = colName.substring(dot + 1); |
242 | ||
243 | 0 | return getColumn(table, col); |
244 | } |
|
245 | ||
246 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | if (singleTable) |
286 | { |
|
287 | 0 | return tableName; |
288 | } |
|
289 | else |
|
290 | { |
|
291 | 0 | 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 | 0 | Enumeration e = tableHash.keys(); |
303 | 0 | String [] tableNames = new String[tableHash.size()]; |
304 | ||
305 | 0 | for (int i = 0; e.hasMoreElements(); i++) |
306 | { |
|
307 | 0 | tableNames[i] = (String) e.nextElement(); |
308 | } |
|
309 | ||
310 | 0 | 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 | 0 | int dot = colName.indexOf('.'); |
327 | ||
328 | 0 | if (dot > 0) |
329 | { |
|
330 | 0 | String table = colName.substring(0, dot); |
331 | 0 | String col = colName.substring(dot + 1); |
332 | ||
333 | 0 | return index(table, col); |
334 | } |
|
335 | ||
336 | 0 | for (int i = 1; i <= numberOfColumns(); i++) |
337 | { |
|
338 | 0 | if (columns[i].name().equalsIgnoreCase(colName)) |
339 | { |
|
340 | 0 | return i; |
341 | } |
|
342 | } |
|
343 | ||
344 | 0 | 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 | 0 | for (int i = 1; i <= numberOfColumns(); i++) |
361 | { |
|
362 | 0 | if (columns[i].name().equalsIgnoreCase(colName) && columns[i].getTableName().equalsIgnoreCase(tableName)) |
363 | { |
|
364 | 0 | return i; |
365 | } |
|
366 | } |
|
367 | ||
368 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | this.numberOfColumns = meta.getColumnCount(); |
404 | 0 | columns = new Column[numberOfColumns() + 1]; |
405 | ||
406 | 0 | for (int i = 1; i <= numberOfColumns(); i++) |
407 | { |
|
408 | 0 | Column col = new Column(); |
409 | 0 | col.populate(meta, i, tableName); |
410 | 0 | columns[i] = col; |
411 | ||
412 | 0 | if ((i > 1) && !col.getTableName().equalsIgnoreCase(columns[i - 1].getTableName())) |
413 | { |
|
414 | 0 | singleTable = false; |
415 | } |
|
416 | } |
|
417 | ||
418 | // Avoid creating a Hashtable in the most common case where only one |
|
419 | // table is involved, even though this makes the multiple table case |
|
420 | // more expensive because the table/column info is duplicated. |
|
421 | 0 | if (singleTable) |
422 | { |
|
423 | // If available, use a the caller supplied table name. |
|
424 | 0 | if ((tableName != null) && (tableName.length() > 0)) |
425 | { |
|
426 | 0 | setTableName(tableName); |
427 | } |
|
428 | else |
|
429 | { |
|
430 | // Since there's only one table involved, attempt to set the |
|
431 | // table name to that of the first column. Sybase jConnect |
|
432 | // 5.2 and older will fail, in which case we are screwed. |
|
433 | try |
|
434 | { |
|
435 | 0 | setTableName(meta.getTableName(1)); |
436 | } |
|
437 | 0 | catch (Exception e) |
438 | { |
|
439 | 0 | setTableName(""); |
440 | 0 | } |
441 | } |
|
442 | } |
|
443 | else |
|
444 | { |
|
445 | 0 | tableHash = new Hashtable((int) ((1.25 * numberOfColumns) + 1)); |
446 | ||
447 | 0 | for (int i = 1; i <= numberOfColumns(); i++) |
448 | { |
|
449 | 0 | if (tableHash.containsKey(columns[i].getTableName())) |
450 | { |
|
451 | 0 | ((Hashtable) tableHash.get(columns[i].getTableName())).put(columns[i].name(), columns[i]); |
452 | } |
|
453 | else |
|
454 | { |
|
455 | 0 | Hashtable columnHash = new Hashtable((int) ((1.25 * numberOfColumns) + 1)); |
456 | 0 | columnHash.put(columns[i].name(), columns[i]); |
457 | 0 | tableHash.put(columns[i].getTableName(), columnHash); |
458 | } |
|
459 | } |
|
460 | } |
|
461 | 0 | } |
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 | 0 | this.columnsAttribute = attributes; |
471 | 0 | } |
472 | ||
473 | /** |
|
474 | * Sets the table name that this Schema represents |
|
475 | * |
|
476 | * @param tableName |
|
477 | */ |
|
478 | void setTableName(String tableName) |
|
479 | { |
|
480 | 0 | this.tableName = tableName; |
481 | 0 | } |
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 | 0 | 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 | 0 | ByteArrayOutputStream bout = new ByteArrayOutputStream(); |
504 | 0 | PrintWriter out = new PrintWriter(bout); |
505 | 0 | out.print('{'); |
506 | ||
507 | 0 | for (int i = 1; i <= numberOfColumns; i++) |
508 | { |
|
509 | 0 | out.print('\''); |
510 | ||
511 | 0 | if (!singleTable) |
512 | { |
|
513 | 0 | out.print(columns[i].getTableName() + '.'); |
514 | } |
|
515 | ||
516 | 0 | out.print(columns[i].name() + '\''); |
517 | ||
518 | 0 | if (i < numberOfColumns) |
519 | { |
|
520 | 0 | out.print(','); |
521 | } |
|
522 | } |
|
523 | ||
524 | 0 | out.print('}'); |
525 | 0 | out.flush(); |
526 | ||
527 | 0 | return bout.toString(); |
528 | } |
|
529 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |