001: /*
002: * This is free software, licensed under the Gnu Public License (GPL)
003: * get a copy from <http://www.gnu.org/licenses/gpl.html>
004: *
005: * author: Henner Zeller <H.Zeller@acm.org>
006: */
007: package henplus.commands;
008:
009: import henplus.AbstractCommand;
010: import henplus.HenPlus;
011: import henplus.Interruptable;
012: import henplus.SQLSession;
013: import henplus.SigIntHandler;
014: import henplus.view.util.NameCompleter;
015:
016: import java.sql.Connection;
017: import java.sql.DatabaseMetaData;
018: import java.sql.ResultSet;
019: import java.util.Collection;
020: import java.util.HashMap;
021: import java.util.HashSet;
022: import java.util.Iterator;
023: import java.util.Map;
024: import java.util.Set;
025: import java.util.SortedSet;
026:
027: /**
028: * FIXME: use SQLMetaData stuff instead.
029: */
030: public class ListUserObjectsCommand extends AbstractCommand implements
031: Interruptable {
032: final private static String[] LIST_TABLES_VIEWS = { "TABLE", "VIEW" };
033: final private static String[] LIST_TABLES = { "TABLE" };
034: final private static String[] LIST_VIEWS = { "VIEW" };
035: final private static int[] TABLE_DISP_COLS = { 2, 3, 4, 5 };
036: final private static int[] PROC_DISP_COLS = { 2, 3, 8 };
037:
038: /**
039: * all tables in one session.
040: */
041: final private Map/*<SQLSession,SortedMap>*/sessionTables;
042: final private Map/*<SQLSession,SortedMap>*/sessionColumns;
043: final private HenPlus henplus;
044:
045: private boolean interrupted;
046:
047: public ListUserObjectsCommand(HenPlus hp) {
048: sessionTables = new HashMap();
049: sessionColumns = new HashMap();
050: henplus = hp;
051: interrupted = false;
052: }
053:
054: /**
055: * returns the command-strings this command can handle.
056: */
057: public String[] getCommandList() {
058: return new String[] { "tables", "views", "procedures", "rehash" };
059: }
060:
061: /**
062: * execute the command given.
063: */
064: public int execute(SQLSession session, String cmd, String param) {
065: if (cmd.equals("rehash")) {
066: rehash(session);
067: } else {
068: try {
069: Connection conn = session.getConnection(); // use createStmt
070: DatabaseMetaData meta = conn.getMetaData();
071: String catalog = conn.getCatalog();
072: /*
073: HenPlus.msg().println("catalog: " + catalog);
074: ResultSetRenderer catalogrenderer =
075: new ResultSetRenderer(meta.getSchemas(), "|", true, true,
076: 2000, HenPlus.out());
077: catalogrenderer.execute();
078: */
079: ResultSetRenderer renderer;
080: ResultSet rset;
081: String objectType;
082: int[] columnDef;
083: if ("procedures".equals(cmd)) {
084: objectType = "Procecdures";
085: HenPlus.msg().println(objectType);
086: rset = meta.getProcedures(catalog, null, null);
087: columnDef = PROC_DISP_COLS;
088: } else {
089: boolean showViews = "views".equals(cmd);
090: objectType = ((showViews) ? "Views" : "Tables");
091: HenPlus.msg().println(objectType);
092: rset = meta.getTables(catalog, null, null,
093: (showViews) ? LIST_VIEWS : LIST_TABLES);
094: columnDef = TABLE_DISP_COLS;
095: }
096:
097: renderer = new ResultSetRenderer(rset, "|", true, true,
098: 10000, HenPlus.out(), columnDef);
099: renderer.getDisplayMetaData()[2].setAutoWrap(78);
100:
101: int tables = renderer.execute();
102: if (tables > 0) {
103: HenPlus.msg().println(
104: tables + " " + objectType + " found.");
105: if (renderer.limitReached()) {
106: HenPlus
107: .msg()
108: .println(
109: "..and probably more; reached display limit");
110: }
111: }
112: } catch (Exception e) {
113: HenPlus.msg().println(e.getMessage());
114: return EXEC_FAILED;
115: }
116: }
117: return SUCCESS;
118: }
119:
120: private NameCompleter getTableCompleter(SQLSession session) {
121: NameCompleter compl = (NameCompleter) sessionTables
122: .get(session);
123: return (compl == null) ? rehash(session) : compl;
124: }
125:
126: private NameCompleter getAllColumnsCompleter(SQLSession session) {
127: NameCompleter compl = (NameCompleter) sessionColumns
128: .get(session);
129: if (compl != null) {
130: return compl;
131: }
132: /*
133: * This may be a lengthy process..
134: */
135: interrupted = false;
136: SigIntHandler.getInstance().pushInterruptable(this );
137: NameCompleter tables = getTableCompleter(session);
138: if (tables == null)
139: return null;
140: Iterator table = tables.getAllNamesIterator();
141: compl = new NameCompleter();
142: while (!interrupted && table.hasNext()) {
143: String tabName = (String) table.next();
144: Collection columns = columnsFor(tabName);
145: Iterator cit = columns.iterator();
146: while (cit.hasNext()) {
147: String col = (String) cit.next();
148: compl.addName(col);
149: }
150: }
151: if (interrupted) {
152: compl = null;
153: } else {
154: sessionColumns.put(session, compl);
155: }
156: SigIntHandler.getInstance().popInterruptable();
157: return compl;
158: }
159:
160: public void unhash(SQLSession session) {
161: sessionTables.remove(session);
162: }
163:
164: /**
165: * rehash table names.
166: */
167: private NameCompleter rehash(SQLSession session) {
168: NameCompleter result = new NameCompleter();
169: Connection conn = session.getConnection(); // use createStmt
170: ResultSet rset = null;
171: try {
172: DatabaseMetaData meta = conn.getMetaData();
173: rset = meta.getTables(null, null, null, LIST_TABLES_VIEWS);
174: while (rset.next()) {
175: result.addName(rset.getString(3));
176: }
177: } catch (Exception e) {
178: // ignore.
179: } finally {
180: if (rset != null) {
181: try {
182: rset.close();
183: } catch (Exception e) {
184: }
185: }
186: }
187: sessionTables.put(session, result);
188: sessionColumns.remove(session);
189: return result;
190: }
191:
192: /**
193: * fixme: add this to the cached values determined by rehash.
194: */
195: public Collection columnsFor(String tabName) {
196: SQLSession session = henplus.getCurrentSession();
197: Set result = new HashSet();
198: Connection conn = session.getConnection(); // use createStmt
199: ResultSet rset = null;
200:
201: String schema = null;
202: int schemaDelim = tabName.indexOf('.');
203: if (schemaDelim > 0) {
204: schema = tabName.substring(0, schemaDelim);
205: tabName = tabName.substring(schemaDelim + 1);
206: }
207: try {
208: DatabaseMetaData meta = conn.getMetaData();
209: rset = meta.getColumns(conn.getCatalog(), schema, tabName,
210: null);
211: while (rset.next()) {
212: result.add(rset.getString(4));
213: }
214: } catch (Exception e) {
215: // ignore.
216: } finally {
217: if (rset != null) {
218: try {
219: rset.close();
220: } catch (Exception e) {
221: }
222: }
223: }
224: return result;
225: }
226:
227: /**
228: * see, if we find exactly one alternative, that is spelled
229: * correctly. If we have more than one alternative but one, that
230: * has the same length of the requested tablename, return this.
231: */
232: public String correctTableName(String tabName) {
233: Iterator it = completeTableName(HenPlus.getInstance()
234: .getCurrentSession(), tabName);
235: if (it == null)
236: return null;
237: boolean foundSameLengthMatch = false;
238: int count = 0;
239: String correctedName = null;
240: if (it.hasNext()) {
241: String alternative = (String) it.next();
242: boolean sameLength = (alternative != null && alternative
243: .length() == tabName.length());
244:
245: foundSameLengthMatch |= sameLength;
246: ++count;
247: if (correctedName == null || sameLength) {
248: correctedName = alternative;
249: }
250: }
251: return (count == 1 || foundSameLengthMatch) ? correctedName
252: : null;
253: }
254:
255: /**
256: * used from diverse commands that need table name completion.
257: */
258: public Iterator completeTableName(SQLSession session,
259: String partialTable) {
260: if (session == null)
261: return null;
262: NameCompleter completer = getTableCompleter(session);
263: return completer.getAlternatives(partialTable);
264: }
265:
266: public Iterator completeAllColumns(String partialColumn) {
267: SQLSession session = henplus.getCurrentSession();
268: if (session == null)
269: return null;
270: NameCompleter completer = getAllColumnsCompleter(session);
271: return completer.getAlternatives(partialColumn);
272: }
273:
274: public Iterator getTableNamesIteratorForSession(SQLSession session) {
275: return getTableCompleter(session).getAllNamesIterator();
276: }
277:
278: public SortedSet getTableNamesForSession(SQLSession session) {
279: return getTableCompleter(session).getAllNames();
280: }
281:
282: /**
283: * return a descriptive string.
284: */
285: public String getShortDescription() {
286: return "list available user objects";
287: }
288:
289: public String getSynopsis(String cmd) {
290: return cmd;
291: }
292:
293: public String getLongDescription(String cmd) {
294: String dsc;
295: if (cmd.equals("rehash")) {
296: dsc = "\trebuild the internal hash for tablename completion.";
297: } else {
298: dsc = "\tLists all " + cmd + " available in this schema.";
299: }
300: return dsc;
301: }
302:
303: public void interrupt() {
304: interrupted = true;
305: }
306: }
307:
308: /*
309: * Local variables:
310: * c-basic-offset: 4
311: * compile-command: "ant -emacs -find build.xml"
312: * End:
313: */
|