001: /*
002: * SchemaReporter.java
003: *
004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
005: *
006: * Copyright 2002-2008, Thomas Kellerer
007: * No part of this code maybe reused without the permission of the author
008: *
009: * To contact the author please send an email to: support@sql-workbench.net
010: *
011: */
012: package workbench.db.report;
013:
014: import java.awt.event.WindowAdapter;
015: import java.awt.event.WindowEvent;
016: import java.io.BufferedWriter;
017: import java.io.FileOutputStream;
018: import java.io.IOException;
019: import java.io.OutputStreamWriter;
020: import java.io.Writer;
021: import java.sql.SQLException;
022: import java.util.ArrayList;
023: import java.util.List;
024: import javax.swing.JDialog;
025:
026: import javax.swing.JFrame;
027: import workbench.db.DbMetadata;
028: import workbench.db.DbMetadata;
029: import workbench.db.DbObject;
030: import workbench.db.DbSettings;
031:
032: import workbench.db.ProcedureDefinition;
033: import workbench.db.SequenceDefinition;
034: import workbench.db.SequenceReader;
035: import workbench.db.TableIdentifier;
036: import workbench.db.WbConnection;
037: import workbench.gui.WbSwingUtilities;
038: import workbench.gui.dbobjects.ProgressPanel;
039: import workbench.interfaces.Interruptable;
040: import workbench.log.LogMgr;
041: import workbench.resource.ResourceMgr;
042: import workbench.storage.RowActionMonitor;
043: import workbench.util.FileUtil;
044: import workbench.util.StrBuffer;
045: import workbench.util.StrWriter;
046: import workbench.util.WbFile;
047: import workbench.util.WbThread;
048:
049: /**
050: * Generate an report from a selection of database tables
051: * @author support@sql-workbench.net
052: *
053: */
054: public class SchemaReporter implements Interruptable {
055: private WbConnection dbConn;
056: private List<TableIdentifier> tables = new ArrayList<TableIdentifier>();
057: private List<ReportProcedure> procedures = new ArrayList<ReportProcedure>();
058: private List<ReportSequence> sequences = new ArrayList<ReportSequence>();
059: private String xmlNamespace;
060: private String[] types;
061: private List<String> schemas;
062:
063: private TagWriter tagWriter = new TagWriter();
064: private RowActionMonitor monitor;
065: private String outputfile;
066: protected boolean cancel = false;
067: private boolean includeTables = true;
068: private boolean includeProcedures = false;
069: private boolean includeGrants = false;
070: private boolean includeSequences = false;
071: private ProgressPanel progressPanel;
072: protected JDialog progressWindow;
073: private boolean showProgress = false;
074: private String schemaNameToUse = null;
075: private JFrame parentWindow;
076: private boolean dbDesignerFormat = false;
077:
078: /**
079: * Creates a new SchemaReporter for the supplied connection
080: * @param conn The connection that the schema report should use
081: */
082: public SchemaReporter(WbConnection conn) {
083: this .dbConn = conn;
084: // Initialize the types to retrieve
085: setIncludeViews(true);
086: }
087:
088: public void setProgressMonitor(RowActionMonitor mon) {
089: this .monitor = mon;
090: }
091:
092: public void setObjectList(List<? extends DbObject> objectList) {
093: if (this .tables == null)
094: this .tables = new ArrayList<TableIdentifier>();
095:
096: for (DbObject dbo : objectList) {
097: if (dbo instanceof TableIdentifier) {
098: for (String type : this .types) {
099: if (dbo.getObjectType().equalsIgnoreCase(type)) {
100: this .tables.add((TableIdentifier) dbo);
101: break;
102: }
103: }
104: }
105: }
106: }
107:
108: /**
109: * Define the list of tables to run the report on
110: */
111: public void setTableList(TableIdentifier[] tableList) {
112: if (this .tables == null)
113: this .tables = new ArrayList<TableIdentifier>();
114: for (int i = 0; i < tableList.length; i++) {
115: if (tableList[i].getTableName().indexOf('%') > -1) {
116: List<TableIdentifier> tlist = retrieveWildcardTables(tableList[i]
117: .getTableName());
118: if (tlist != null) {
119: this .tables.addAll(tlist);
120: }
121: } else {
122: this .tables.add(tableList[i]);
123: }
124: }
125: }
126:
127: public void setSchemas(List<String> list) {
128: if (list == null || list.size() == 0)
129: return;
130: this .schemas = list;
131: this .schemaNameToUse = null;
132: }
133:
134: public void setSchemaNameToUse(String name) {
135: if (this .schemas == null || this .schemas.size() == 1) {
136: this .schemaNameToUse = name;
137: }
138: }
139:
140: public void setIncludeViews(boolean flag) {
141: if (flag) {
142: types = new String[3];
143: types[0] = this .dbConn.getMetadata().getTableTypeName();
144: types[1] = this .dbConn.getMetadata().getViewTypeName();
145: types[2] = DbMetadata.MVIEW_NAME;
146: } else {
147: types = new String[3];
148: types[0] = this .dbConn.getMetadata().getTableTypeName();
149: }
150: }
151:
152: public void setIncludeSequences(boolean flag) {
153: this .includeSequences = flag;
154: }
155:
156: public void setIncludeTables(boolean flag) {
157: this .includeTables = flag;
158: }
159:
160: public void setIncludeProcedures(boolean flag) {
161: this .includeProcedures = flag;
162: }
163:
164: public void setIncludeGrants(boolean flag) {
165: this .includeGrants = flag;
166: }
167:
168: public void setOutputFilename(String filename) {
169: this .outputfile = filename;
170: }
171:
172: /**
173: * Return the XML for the (selected) tables
174: * @return The XML for all tables
175: */
176: public String getXml() {
177: this .cancel = false;
178: StrWriter out = new StrWriter(5000);
179: try {
180: this .writeXml(out);
181: } catch (Exception e) {
182: // Cannot happen with StrWriter
183: }
184: return out.toString();
185: }
186:
187: public void setDbDesigner(boolean flag) {
188: this .dbDesignerFormat = flag;
189: }
190:
191: public void setShowProgress(boolean flag, JFrame parent) {
192: this .showProgress = flag;
193: this .parentWindow = parent;
194: }
195:
196: public void writeXml() throws IOException, SQLException {
197:
198: if (this .showProgress) {
199: // This is the only way I can figure out to show
200: // the progress as as modal window, but let the
201: // calling thread proceed with the work.
202: Thread t = new WbThread("ShowProgress") {
203: public void run() {
204: openProgressMonitor();
205: }
206: };
207: t.start();
208: }
209:
210: BufferedWriter bw = null;
211:
212: try {
213: bw = new BufferedWriter(new OutputStreamWriter(
214: new FileOutputStream(this .outputfile), "UTF-8"),
215: 16 * 1024);
216: this .writeXml(bw);
217: } catch (IOException io) {
218: LogMgr.logError("WbSchemaReport.execute()",
219: "Error writing report", io);
220: throw io;
221: } finally {
222: FileUtil.closeQuitely(bw);
223: closeProgress();
224: }
225: }
226:
227: /**
228: * Write the XML into the supplied output
229: */
230: public void writeXml(Writer out) throws IOException, SQLException {
231: this .cancel = false;
232:
233: if (this .includeTables && this .tables.size() == 0)
234: this .retrieveTables();
235: if (this .cancel)
236: return;
237:
238: if (this .dbDesignerFormat) {
239: WbFile f = new WbFile(this .outputfile);
240: DbDesignerWriter writer = new DbDesignerWriter(this .dbConn,
241: this .tables, f.getFileName());
242: writer.setMonitor(monitor);
243: writer.setProgressPanel(progressPanel);
244: writer.writeXml(out);
245: return;
246: }
247:
248: if (this .includeProcedures && this .procedures.size() == 0)
249: this .retrieveProcedures();
250: if (this .cancel)
251: return;
252:
253: if (this .includeSequences && this .sequences.size() == 0)
254: this .retrieveSequences();
255: if (this .cancel)
256: return;
257:
258: out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
259: out.write("<");
260: if (this .xmlNamespace != null) {
261: out.write(this .xmlNamespace);
262: out.write(':');
263: }
264: out.write("schema-report>\n\n");
265: writeReportInfo(out);
266: out.write("\n");
267:
268: int count = this .tables.size();
269: int totalCount = count + this .procedures.size()
270: + this .sequences.size();
271: int totalCurrent = 1;
272:
273: DbSettings dbs = dbConn.getMetadata().getDbSettings();
274:
275: for (TableIdentifier table : tables) {
276: try {
277: if (this .cancel)
278: break;
279:
280: String tableName = table.getTableExpression();
281: if (this .monitor != null) {
282: this .monitor.setCurrentObject(tableName,
283: totalCurrent, totalCount);
284: }
285: if (this .progressPanel != null) {
286: this .progressPanel.setInfoText(tableName);
287: }
288:
289: String type = table.getType();
290: if (type == null) {
291: type = this .dbConn.getMetadata().getObjectType(
292: table);
293: table.setType(type);
294: }
295:
296: if (dbs.isViewType(type)) {
297: ReportView rview = new ReportView(table,
298: this .dbConn, true, this .xmlNamespace);
299: rview.setSchemaNameToUse(this .schemaNameToUse);
300: rview.writeXml(out);
301: } else {
302: ReportTable rtable = new ReportTable(table,
303: this .dbConn, this .xmlNamespace, true, true,
304: true, true, this .includeGrants);
305: rtable.setSchemaNameToUse(this .schemaNameToUse);
306: rtable.writeXml(out);
307: rtable.done();
308: }
309: out.flush();
310: totalCurrent++;
311: } catch (Exception e) {
312: LogMgr.logError("SchemaReporter.writeXml()",
313: "Error writing table: " + table, e);
314: }
315: }
316: count = this .procedures.size();
317: if (count > 0)
318: out.write("\n");
319: for (ReportProcedure proc : procedures) {
320: if (this .monitor != null) {
321: this .monitor.setCurrentObject(proc.getProcedureName(),
322: totalCurrent, totalCount);
323: }
324: if (this .progressPanel != null) {
325: this .progressPanel.setInfoText(proc.getProcedureName());
326: }
327: proc.writeXml(out);
328: out.write('\n');
329: totalCurrent++;
330: if (this .cancel)
331: break;
332: }
333:
334: count = this .sequences.size();
335: if (count > 0)
336: out.write("\n");
337: for (ReportSequence seq : sequences) {
338: String name = seq.getSequence().getSequenceName();
339: if (this .monitor != null) {
340: this .monitor.setCurrentObject(name, totalCurrent,
341: totalCount);
342: }
343: if (this .progressPanel != null) {
344: this .progressPanel.setInfoText(name);
345: }
346: seq.writeXml(out);
347: out.write('\n');
348: totalCurrent++;
349: if (this .cancel)
350: break;
351: }
352:
353: out.write("</");
354: if (this .xmlNamespace != null) {
355: out.write(this .xmlNamespace);
356: out.write(':');
357: }
358: out.write("schema-report>");
359: out.flush();
360: }
361:
362: /**
363: * Writes basic information about the report
364: */
365: private void writeReportInfo(Writer out) throws IOException {
366: StrBuffer info = new StrBuffer();
367: StrBuffer indent = new StrBuffer(" ");
368: info.append(this .dbConn.getDatabaseInfoAsXml(indent,
369: this .xmlNamespace));
370: info.writeTo(out);
371: }
372:
373: /**
374: * Define the namespace for the XML tags
375: * @param name The namespace to be used for the XML tags
376: */
377: public void setNamespace(String name) {
378: this .xmlNamespace = name;
379: this .tagWriter.setNamespace(name);
380: }
381:
382: /**
383: * Get the currently defined namespace
384: * @return The namespace that is used
385: */
386: public String getNamespace() {
387: return this .xmlNamespace;
388: }
389:
390: /**
391: * Cancel the current reporting process (this might leave a corrupted XML file)
392: */
393: public void cancelExecution() {
394: this .cancel = true;
395: closeProgress();
396: }
397:
398: public boolean confirmCancel() {
399: return true;
400: }
401:
402: private void closeProgress() {
403: if (this .progressWindow != null) {
404: this .progressWindow.setVisible(false);
405: this .progressWindow.dispose();
406: }
407: }
408:
409: private void openProgressMonitor() {
410: progressPanel = new ProgressPanel(this );
411: this .progressPanel.setInfoSize(15);
412: this .progressPanel.setRowSize(0);
413: this .progressPanel.setFilename(this .outputfile);
414: this .progressPanel.setInfoText(ResourceMgr
415: .getString("MsgProcessTable"));
416:
417: this .progressWindow = new JDialog(this .parentWindow, true);
418: this .progressWindow
419: .setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
420: this .progressWindow.getContentPane().add(progressPanel);
421: this .progressWindow.setTitle(ResourceMgr
422: .getString("MsgReportWindowTitle"));
423:
424: this .progressWindow.addWindowListener(new WindowAdapter() {
425: public void windowClosing(WindowEvent e) {
426: cancel = true;
427: if (progressWindow != null) {
428: progressWindow.setVisible(false);
429: progressWindow.dispose();
430: }
431: }
432: });
433:
434: WbSwingUtilities.invoke(new Runnable() {
435: public void run() {
436: progressWindow.pack();
437: progressWindow.setSize(300, 120);
438: WbSwingUtilities.center(progressWindow, parentWindow);
439: progressWindow.setVisible(true);
440: }
441: });
442: }
443:
444: private void retrieveTables() {
445: if (this .monitor != null) {
446: this .monitor.setCurrentObject(ResourceMgr
447: .getString("MsgSchemaReporterRetrievingTables"),
448: -1, -1);
449: }
450: if (this .schemas == null || this .schemas.size() == 0) {
451: this .retrieveTables(null);
452: } else {
453: int count = this .schemas.size();
454: for (int i = 0; i < count; i++) {
455: this .retrieveTables(schemas.get(i));
456: }
457: }
458: if (this .monitor != null) {
459: this .monitor.setCurrentObject(null, -1, -1);
460: }
461: }
462:
463: private List<TableIdentifier> retrieveWildcardTables(String name) {
464: try {
465: String schema = this .dbConn.getMetadata()
466: .adjustSchemaNameCase(name);
467: return this .dbConn.getMetadata().getTableList(null, schema,
468: this .types);
469: } catch (SQLException e) {
470: LogMgr.logError("SchemaReporter.retrieveWildcardTables()",
471: "Error retrieving tables", e);
472: return null;
473: }
474: }
475:
476: private void retrieveSequences() {
477: if (this .monitor != null) {
478: this .monitor
479: .setCurrentObject(
480: ResourceMgr
481: .getString("MsgSchemaReporterRetrievingProcedures"),
482: -1, -1);
483: }
484: if (this .schemas == null || this .schemas.size() == 0) {
485: this .retrieveSequences(null);
486: } else {
487: for (String schema : schemas) {
488: this .retrieveSequences(schema);
489: }
490: }
491: if (this .monitor != null) {
492: this .monitor.setCurrentObject(null, -1, -1);
493: }
494: }
495:
496: private void retrieveSequences(String targetSchema) {
497: try {
498: String schema = this .dbConn.getMetadata()
499: .adjustSchemaNameCase(targetSchema);
500: if (schema == null) {
501: schema = this .dbConn.getMetadata().getSchemaToUse();
502: }
503: SequenceReader reader = this .dbConn.getMetadata()
504: .getSequenceReader();
505: if (reader == null)
506: return;
507:
508: List<SequenceDefinition> seqs = reader.getSequences(schema);
509:
510: for (SequenceDefinition seq : seqs) {
511: ReportSequence rseq = new ReportSequence(seq,
512: this .xmlNamespace);
513: this .sequences.add(rseq);
514: if (this .cancel)
515: return;
516: }
517: } catch (Exception e) {
518: LogMgr.logError("SchemaReporter.retrieveSequences()",
519: "Error retrieving sequences", e);
520: }
521: }
522:
523: private void retrieveProcedures() {
524: if (this .monitor != null) {
525: this .monitor
526: .setCurrentObject(
527: ResourceMgr
528: .getString("MsgSchemaReporterRetrievingProcedures"),
529: -1, -1);
530: }
531: if (this .schemas == null || this .schemas.size() == 0) {
532: this .retrieveProcedures(null);
533: } else {
534: for (String schema : schemas) {
535: this .retrieveProcedures(schema);
536: }
537: }
538: if (this .monitor != null) {
539: this .monitor.setCurrentObject(null, -1, -1);
540: }
541: }
542:
543: private void retrieveProcedures(String targetSchema) {
544: try {
545: String schema = this .dbConn.getMetadata()
546: .adjustSchemaNameCase(targetSchema);
547: List<ProcedureDefinition> procs = this .dbConn.getMetadata()
548: .getProcedureList(null, schema);
549:
550: for (ProcedureDefinition def : procs) {
551: ReportProcedure proc = new ReportProcedure(def,
552: this .dbConn);
553: this .procedures.add(proc);
554: if (this .cancel)
555: return;
556: }
557: } catch (SQLException e) {
558: LogMgr.logError("SchemaReporter.retrieveProcedures()",
559: "Error retrieving procedures", e);
560: }
561: }
562:
563: /**
564: * Retrieve all tables for the current user.
565: * The "type" of table can be defined by #setTableTypes(String)
566: */
567: private void retrieveTables(String targetSchema) {
568: try {
569: if (this .cancel)
570: return;
571: String schema = this .dbConn.getMetadata()
572: .adjustSchemaNameCase(targetSchema);
573: this .tables = this .dbConn.getMetadata().getTableList(
574: schema, this .types);
575: } catch (SQLException e) {
576: LogMgr.logError("SchemaReporter.retrieveTables()",
577: "Error retrieving tables", e);
578: }
579: }
580:
581: }
|