1 /***
2 * Copyright 2004 Steven Caswell
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.mungoknotwise.sqlcli;
17
18 import java.sql.DatabaseMetaData;
19 import java.sql.ResultSet;
20 import java.sql.ResultSetMetaData;
21 import java.sql.SQLException;
22 import java.sql.Types;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import org.apache.commons.beanutils.DynaBean;
28 import org.apache.commons.beanutils.DynaProperty;
29 import org.apache.commons.beanutils.RowSetDynaClass;
30 import org.apache.commons.lang.StringUtils;
31 import org.apache.log4j.Logger;
32
33 /***
34 * An implementation of the {@link ResultSetExtractor} that writes the results
35 * of a describe command to the provided print streams.
36 *
37 * @author Steven Caswell
38 * @version $Id: PrintStreamedDescribeResultSetExtractor.java,v 1.4 2004/10/20 21:41:32 mungoknotwise Exp $
39 */
40 public class PrintStreamedDescribeResultSetExtractor
41 extends BasicPrintStreamed
42 implements ResultSetExtractor
43 {
44
45
46
47
48
49 private static Logger logger = Logger.getLogger("DEBUG." + PrintStreamedDescribeResultSetExtractor.class.getName());
50
51
52
53
54
55
56
57
58
59 private static final int COLUMN_COUNT = 4;
60 private static final int META_DATA_COLUMN_NAME_INDEX = 4;
61 private static final int META_DATA_COLUMN_TYPE_INDEX = 6;
62 private static final int META_DATA_DEFAULT_VALUE_INDEX = 13;
63
64 private static final int NULL_VALUE_DISPLAY_SIZE = 4;
65 private static final int DEFAULT_VALUE_MINIMUM_DISPLAY_SIZE = 7;
66
67 private static final int DISPLAY_COLUMN_NAME_INDEX = 0;
68 private static final int DISPLAY_COLUMN_TYPE_INDEX = 1;
69 private static final int DISPLAY_NULLABLE_INDEX = 2;
70 private static final int DISPLAY_DEFAULT_VALUE_INDEX = 3;
71
72 private static final int COLUMN_NAME_INDEX = 4;
73 private static final int DATA_TYPE_INDEX = 5;
74 private static final int COLUMN_TYPE_INDEX = 6;
75 private static final int COLUMN_SIZE_INDEX = 7;
76 private static final int DECIMAL_DIGITS_INDEX = 9;
77 private static final int DEFAULT_VALUE_INDEX = 13;
78 private static final int NULLABLE_INDEX = 18;
79
80
81
82
83
84 private Map attributes;
85 private int columnCount;
86 private int columnNameColumnSize;
87 private int columnTypeColumnSize;
88 private int defaultValueColumnSize;
89 private int[] displaySize;
90 private String fieldSeparator = "|";
91 private String headerSeparator = "+";
92 private int[] justify;
93 private String[] label;
94
95
96
97
98
99 /***
100 * Constructs a new instance of
101 * <code>PrintStreamedDescribeResultSetExtractor</code>.
102 */
103 public PrintStreamedDescribeResultSetExtractor()
104 {
105 this.attributes = new HashMap();
106 }
107
108
109
110
111
112
113
114
115 /***
116 * {@inheritDoc}
117 */
118 public Object extractData(final ResultSet resultSet) throws SQLException
119 {
120 if(resultSet == null)
121 {
122 throw new IllegalArgumentException("resultSet is null");
123 }
124 ResultSetMetaData metaData = resultSet.getMetaData();
125 if(logger.isDebugEnabled())
126 {
127 logger.debug("result set meta data " + (metaData == null ? "is" : "is not") + " null");
128 }
129
130 RowSetDynaClass rsdc = new RowSetDynaClass(resultSet);
131 if(logger.isDebugEnabled())
132 {
133 logger.debug("row set dyna class " + (rsdc == null ? "is" : "is not") + " null");
134 }
135 DynaProperty[] properties = rsdc.getDynaProperties();
136 if(logger.isDebugEnabled())
137 {
138 logger.debug("properties length: " + properties.length);
139 for(int i = 0; i < properties.length; i++)
140 {
141 logger.debug("property name: " + properties[i].getName() + " class: " + properties[i].getType());
142 }
143 }
144 this.configureOutput(metaData, rsdc);
145 if(logger.isDebugEnabled())
146 {
147 logger.debug("output has been configured");
148 }
149
150 boolean found = false;
151 boolean needsHeader = true;
152 boolean needsFooter = true;
153
154 List rows = rsdc.getRows();
155 if(logger.isDebugEnabled())
156 {
157 logger.debug("row count: " + rows.size());
158 }
159
160 for(Iterator iter = rows.iterator(); iter.hasNext();)
161 {
162 found = true;
163 if(needsHeader)
164 {
165 this.writeHeader();
166 needsHeader = false;
167 }
168 DynaBean row = (DynaBean) iter.next();
169 if(logger.isDebugEnabled())
170 {
171 logger.debug("row values");
172 for(int i = 0; i < properties.length; i++)
173 {
174 logger.debug("property '" + properties[i].getName() + "': " + row.get(properties[i].getName()));
175 }
176 }
177
178 int columnSize = -1;
179 if(row.get("column_size") != null)
180 {
181 columnSize = ((Number) row.get("column_size")).intValue();
182 }
183 if(logger.isDebugEnabled())
184 {
185 logger.debug("column size: " + columnSize);
186 }
187 int decimalDigits = -1;
188 if(row.get("decimal_digits") != null)
189 {
190 decimalDigits = ((Number) row.get("decimal_digits")).intValue();
191 }
192 if(logger.isDebugEnabled())
193 {
194 logger.debug("decimal digits: " + decimalDigits);
195 }
196 int dataType = ((Number) row.get("data_type")).intValue();
197 String columnName = (String) row.get("column_name");
198 String columnType = (String) row.get("type_name");
199 String defaultValue = (String) row.get("column_def");
200 String isNullable = (String) row.get("is_nullable");
201 if(logger.isDebugEnabled())
202 {
203 logger.debug("column name: " + columnName);
204 logger.debug("column type: " + columnType);
205 logger.debug("default value: " + (defaultValue == null ? "null" : defaultValue));
206 logger.debug("is nullable: " + isNullable);
207 }
208 this.print(this.getFieldSeparator());
209 this.print(StringUtils.rightPad(columnName, this.displaySize[DISPLAY_COLUMN_NAME_INDEX]));
210 this.print(this.getFieldSeparator());
211 if(dataType == Types.DATE)
212 {
213
214 }
215 else if(dataType == Types.TIME ||
216 dataType == Types.TIMESTAMP
217 )
218 {
219 columnType += "(" + columnSize + ")";
220 }
221 else
222 {
223 if(decimalDigits <= 0)
224 {
225 columnType += "(" + columnSize + ")";
226 }
227 else
228 {
229 columnType += "(" + columnSize + "," + decimalDigits + ")";
230 }
231 }
232 this.print(StringUtils.rightPad(columnType, this.displaySize[DISPLAY_COLUMN_TYPE_INDEX]));
233 this.print(this.getFieldSeparator());
234 this.print(StringUtils.rightPad(isNullable, this.displaySize[DISPLAY_NULLABLE_INDEX]));
235 this.print(this.getFieldSeparator());
236 this.print(StringUtils.rightPad(defaultValue == null ? "NULL" : defaultValue, this.displaySize[DISPLAY_DEFAULT_VALUE_INDEX]));
237 this.println(this.getFieldSeparator());
238 }
239 if(found)
240 {
241 this.writeFooter();
242 }
243 else
244 {
245 String tableName = (String) this.attributes.get(DescribeCommand.TABLE_NAME_KEY);
246 if(!StringUtils.isEmpty(tableName))
247 {
248 this.print("Table or view '");
249 this.print(tableName);
250 this.println("' does not exist");
251 }
252 }
253
254 return null;
255 }
256
257 /***
258 * {@inheritDoc}
259 */
260 public void setAttribute(final String key, final Object value)
261 {
262 this.attributes.put(key, value);
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 private void configureOutput(final ResultSetMetaData metaData, final RowSetDynaClass rsdc)
290 throws SQLException
291 {
292 if(metaData == null)
293 {
294 throw new IllegalArgumentException("metaData is null");
295 }
296 if(rsdc == null)
297 {
298 throw new IllegalArgumentException("rsds is null");
299 }
300 this.columnCount = COLUMN_COUNT;
301
302 this.displaySize = new int[COLUMN_COUNT];
303 this.displaySize[DISPLAY_COLUMN_NAME_INDEX] = metaData.getColumnDisplaySize(META_DATA_COLUMN_NAME_INDEX);
304 this.displaySize[DISPLAY_COLUMN_TYPE_INDEX] = metaData.getColumnDisplaySize(META_DATA_COLUMN_TYPE_INDEX);
305 this.displaySize[DISPLAY_NULLABLE_INDEX] = NULL_VALUE_DISPLAY_SIZE;
306 defaultValueColumnSize = metaData.getColumnDisplaySize(META_DATA_DEFAULT_VALUE_INDEX);
307 if(defaultValueColumnSize < DEFAULT_VALUE_MINIMUM_DISPLAY_SIZE)
308 {
309 defaultValueColumnSize = DEFAULT_VALUE_MINIMUM_DISPLAY_SIZE;
310 }
311 this.displaySize[DISPLAY_DEFAULT_VALUE_INDEX] = defaultValueColumnSize;
312 this.label = new String[]
313 { "Column Name"
314 , "Data Type"
315 , "Null"
316 , "Default"
317 };
318
319
320 List rows = rsdc.getRows();
321 if(logger.isDebugEnabled())
322 {
323 logger.debug("rows size: " + (rows == null ? "null" : "" + rows.size()));
324 }
325 for(Iterator iter = rows.iterator(); iter.hasNext();)
326 {
327 DynaBean row = (DynaBean) iter.next();
328 if(logger.isDebugEnabled())
329 {
330 logger.debug("row == null? " + (row == null));
331 }
332 String typeName = (String) row.get("type_name");
333 if(logger.isDebugEnabled())
334 {
335 logger.debug("type name: " + typeName);
336 }
337 int dataType = -1;
338 if(row.get("data_type") != null)
339 {
340 dataType = ((Number) row.get("data_type")).intValue();
341 }
342 int columnSize = -1;
343 if(row.get("column_size") != null)
344 {
345 columnSize = ((Number) row.get("column_size")).intValue();
346 }
347 int decimalDigits = -1;
348 if(row.get("decimal_digits") != null)
349 {
350 decimalDigits = ((Number) row.get("decimal_digits")).intValue();
351 }
352 if(logger.isDebugEnabled())
353 {
354 logger.debug("data type: " + dataType);
355 logger.debug("column size: " + columnSize);
356 logger.debug("decimal digits: " + decimalDigits);
357 }
358 int columnSizeLength = 0;
359 if(dataType == Types.TIME ||
360 dataType == Types.TIMESTAMP
361 )
362 {
363 columnSizeLength = Integer.toString(columnSize).length();
364 }
365 else
366 {
367 if(decimalDigits <= 0)
368 {
369 columnSizeLength = Integer.toString(columnSize).length();
370 }
371 else
372 {
373 columnSizeLength = Integer.toString(columnSize).length() + 1 + Integer.toString(decimalDigits).length();
374 }
375 }
376 columnSizeLength += 2;
377 columnSizeLength += typeName.length();
378 if(this.displaySize[DISPLAY_COLUMN_TYPE_INDEX] < columnSizeLength)
379 {
380 this.displaySize[DISPLAY_COLUMN_TYPE_INDEX] = columnSizeLength;
381 }
382 }
383 if(logger.isDebugEnabled())
384 {
385 logger.debug("calculated column type display size: " + this.displaySize[DISPLAY_COLUMN_TYPE_INDEX]);
386 }
387 }
388
389 private void configureOutput(final ResultSetMetaData metaData) throws SQLException
390 {
391 this.columnCount = COLUMN_COUNT;
392
393 this.displaySize = new int[COLUMN_COUNT];
394 this.displaySize[DISPLAY_COLUMN_NAME_INDEX] = metaData.getColumnDisplaySize(META_DATA_COLUMN_NAME_INDEX);
395 this.displaySize[DISPLAY_COLUMN_TYPE_INDEX] = metaData.getColumnDisplaySize(META_DATA_COLUMN_TYPE_INDEX);
396 this.displaySize[DISPLAY_NULLABLE_INDEX] = NULL_VALUE_DISPLAY_SIZE;
397 defaultValueColumnSize = metaData.getColumnDisplaySize(META_DATA_DEFAULT_VALUE_INDEX);
398 if(defaultValueColumnSize < DEFAULT_VALUE_MINIMUM_DISPLAY_SIZE)
399 {
400 defaultValueColumnSize = DEFAULT_VALUE_MINIMUM_DISPLAY_SIZE;
401 }
402 this.displaySize[DISPLAY_DEFAULT_VALUE_INDEX] = defaultValueColumnSize;
403 this.label = new String[]
404 { "Column Name"
405 , "Data Type"
406 , "Null"
407 , "Default"
408 };
409 }
410
411 private String getFieldSeparator()
412 {
413 return this.fieldSeparator;
414 }
415
416 private String getHeaderSeparator()
417 {
418 return this.headerSeparator;
419 }
420
421 private void writeHeader()
422 {
423 this.println("");
424 this.writeHeaderLine();
425
426 for(int i = 1; i <= columnCount; i++)
427 {
428 this.print(this.getFieldSeparator());
429
430 this.print(StringUtils.rightPad(this.label[i-1], this.displaySize[i-1]));
431 }
432 this.println(this.getFieldSeparator());
433
434 this.writeHeaderLine();
435 }
436
437 private void writeHeaderLine()
438 {
439 for(int i = 1; i <= this.columnCount; i++)
440 {
441 this.print(this.getHeaderSeparator());
442 this.print(StringUtils.repeat("-", this.displaySize[i-1]));
443 }
444 this.println(this.getHeaderSeparator());
445 }
446
447 private void writeFooter()
448 {
449 this.writeHeaderLine();
450 this.println("");
451 }
452
453
454
455
456
457 }