001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.server.web;
007:
008: import java.util.HashMap;
009: import java.util.HashSet;
010: import java.util.Iterator;
011: import java.util.Map;
012: import java.util.Map.Entry;
013:
014: import org.h2.bnf.Bnf;
015: import org.h2.bnf.Rule;
016: import org.h2.bnf.Sentence;
017: import org.h2.command.Parser;
018: import org.h2.message.Message;
019: import org.h2.util.StringUtils;
020:
021: /**
022: * A BNF terminal rule that is linked to the database context information.
023: * This class is used by the H2 Console, to support auto-complete.
024: */
025: public class DbContextRule implements Rule {
026: DbContents contents;
027: int type;
028: static final int COLUMN = 0, TABLE = 1, TABLE_ALIAS = 2;
029: public static final int NEW_TABLE_ALIAS = 3;
030: public static final int COLUMN_ALIAS = 4;
031: private static final boolean SUGGEST_TABLE_ALIAS = false;
032:
033: DbContextRule(DbContents contents, int type) {
034: this .contents = contents;
035: this .type = type;
036: }
037:
038: public String name() {
039: return null;
040: }
041:
042: public String random(Bnf config, int level) {
043: return null;
044: }
045:
046: public Rule last() {
047: return this ;
048: }
049:
050: public void setLinks(HashMap ruleMap) {
051: }
052:
053: public void addNextTokenList(String query, Sentence sentence) {
054: switch (type) {
055: case TABLE:
056: addTable(query, sentence);
057: break;
058: case NEW_TABLE_ALIAS:
059: addNewTableAlias(query, sentence);
060: break;
061: case TABLE_ALIAS:
062: addTableAlias(query, sentence);
063: break;
064: case COLUMN_ALIAS:
065: // addColumnAlias(query, sentence);
066: // break;
067: case COLUMN:
068: addColumn(query, sentence);
069: break;
070: default:
071: }
072: }
073:
074: private void addTableAlias(String query, Sentence sentence) {
075: String q = StringUtils.toUpperEnglish(query.trim());
076: HashMap map = sentence.getAliases();
077: HashSet set = new HashSet();
078: if (map != null) {
079: for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
080: Map.Entry entry = (Entry) it.next();
081: String alias = (String) entry.getKey();
082: DbTableOrView table = (DbTableOrView) entry.getValue();
083: set.add(StringUtils.toUpperEnglish(table.name));
084: if (q.length() == 0 || alias.startsWith(q)) {
085: if (q.length() < alias.length()) {
086: sentence.add(alias + ".", alias.substring(q
087: .length())
088: + ".", Sentence.CONTEXT);
089: }
090: }
091: }
092: }
093: HashSet tables = sentence.getTables();
094: if (tables != null) {
095: for (Iterator it = tables.iterator(); it.hasNext();) {
096: DbTableOrView table = (DbTableOrView) it.next();
097: String tableName = StringUtils
098: .toUpperEnglish(table.name);
099: //DbTableOrView[] tables = contents.defaultSchema.tables;
100: //for(int i=0; i<tables.length; i++) {
101: // DbTableOrView table = tables[i];
102: // String tableName = StringUtils.toUpperEnglish(table.name);
103: if (!set.contains(tableName)) {
104: if (q.length() == 0 || tableName.startsWith(q)) {
105: if (q.length() < tableName.length()) {
106: sentence.add(tableName + ".", tableName
107: .substring(q.length())
108: + ".", Sentence.CONTEXT);
109: }
110: }
111: }
112: }
113: }
114: }
115:
116: private void addNewTableAlias(String query, Sentence sentence) {
117: if (SUGGEST_TABLE_ALIAS) {
118: // good when testing!
119: if (query.length() > 3) {
120: return;
121: }
122: String lastTableName = StringUtils.toUpperEnglish(sentence
123: .getLastTable().name);
124: if (lastTableName == null) {
125: return;
126: }
127: HashMap map = sentence.getAliases();
128: String shortName = lastTableName.substring(0, 1);
129: if (map != null && map.containsKey(shortName)) {
130: int result = 0;
131: for (int i = 1;; i++) {
132: if (!map.containsKey(shortName + i)) {
133: result = i;
134: break;
135: }
136: }
137: shortName += result;
138: }
139: String q = StringUtils.toUpperEnglish(query.trim());
140: if (q.length() == 0
141: || StringUtils.toUpperEnglish(shortName)
142: .startsWith(q)) {
143: if (q.length() < shortName.length()) {
144: sentence.add(shortName, shortName.substring(q
145: .length()), Sentence.CONTEXT);
146: }
147: }
148: }
149: }
150:
151: // private boolean startWithIgnoreCase(String a, String b) {
152: // if(a.length() < b.length()) {
153: // return false;
154: // }
155: // for(int i=0; i<b.length(); i++) {
156: // if(Character.toUpperCase(a.charAt(i))
157: // != Character.toUpperCase(b.charAt(i))) {
158: // return false;
159: // }
160: // }
161: // return true;
162: // }
163:
164: private void addTable(String query, Sentence sentence) {
165: DbSchema schema = contents.defaultSchema;
166: String text = StringUtils.toUpperEnglish(sentence.text).trim();
167: if (text.endsWith(".")) {
168: for (int i = 0; i < contents.schemas.length; i++) {
169: if (text.endsWith(StringUtils
170: .toUpperEnglish(contents.schemas[i].name)
171: + ".")) {
172: schema = contents.schemas[i];
173: break;
174: }
175: }
176: }
177: String q = StringUtils.toUpperEnglish(query.trim());
178: DbTableOrView[] tables = schema.tables;
179: for (int i = 0; i < tables.length; i++) {
180: DbTableOrView table = tables[i];
181: if (q.length() == 0
182: || StringUtils.toUpperEnglish(table.name)
183: .startsWith(q)) {
184: if (q.length() < table.quotedName.length()) {
185: sentence.add(table.quotedName, table.quotedName
186: .substring(q.length()), Sentence.CONTEXT);
187: }
188: }
189: }
190: }
191:
192: private void addColumn(String query, Sentence sentence) {
193: String tableName = query;
194: String columnPattern = "";
195: if (query.trim().length() == 0) {
196: tableName = null;
197: if (sentence.text.trim().endsWith(".")) {
198: return;
199: }
200: } else {
201: tableName = StringUtils.toUpperEnglish(query.trim());
202: if (tableName.endsWith(".")) {
203: tableName = tableName.substring(0,
204: tableName.length() - 1);
205: } else {
206: columnPattern = StringUtils
207: .toUpperEnglish(query.trim());
208: tableName = null;
209: }
210: }
211: HashSet set = null;
212: HashMap aliases = sentence.getAliases();
213: if (tableName == null && sentence.getTables() != null) {
214: set = sentence.getTables();
215: }
216: DbTableOrView table = null;
217: if (tableName != null && aliases != null
218: && aliases.get(tableName) != null) {
219: table = (DbTableOrView) aliases.get(tableName);
220: tableName = StringUtils.toUpperEnglish(table.name);
221: }
222: if (tableName == null) {
223: if (set == null && aliases == null) {
224: return;
225: }
226: if ((set != null && set.size() > 1)
227: || (aliases != null && aliases.size() > 1)) {
228: return;
229: }
230: }
231: if (table == null) {
232: DbTableOrView[] tables = contents.defaultSchema.tables;
233: for (int i = 0; i < tables.length; i++) {
234: DbTableOrView tab = tables[i];
235: String t = StringUtils.toUpperEnglish(tab.name);
236: if (tableName != null && !tableName.equals(t)) {
237: continue;
238: }
239: if (set != null && !set.contains(tab)) {
240: continue;
241: }
242: table = tab;
243: break;
244: }
245: }
246: if (table != null) {
247: for (int j = 0; j < table.columns.length; j++) {
248: String columnName = table.columns[j].name;
249: if (!StringUtils.toUpperEnglish(columnName).startsWith(
250: columnPattern)) {
251: continue;
252: }
253: if (columnPattern.length() < columnName.length()) {
254: sentence.add(columnName, columnName
255: .substring(columnPattern.length()),
256: Sentence.CONTEXT);
257: }
258: }
259: }
260: }
261:
262: public String matchRemove(String query, Sentence sentence) {
263: if (query.length() == 0) {
264: return null;
265: }
266: String s;
267: switch (type) {
268: case TABLE:
269: s = matchTable(query, sentence);
270: break;
271: case NEW_TABLE_ALIAS:
272: s = matchTableAlias(query, sentence, true);
273: break;
274: case TABLE_ALIAS:
275: s = matchTableAlias(query, sentence, false);
276: break;
277: case COLUMN_ALIAS:
278: s = matchColumnAlias(query, sentence, false);
279: break;
280: case COLUMN:
281: s = matchColumn(query, sentence);
282: break;
283: default:
284: throw Message.getInternalError("type=" + type);
285: }
286: return s;
287: }
288:
289: public String matchTable(String query, Sentence sentence) {
290: String up = StringUtils.toUpperEnglish(query);
291: DbTableOrView[] tables = contents.defaultSchema.tables;
292: String best = null;
293: DbTableOrView bestTable = null;
294: for (int i = 0; i < tables.length; i++) {
295: DbTableOrView table = tables[i];
296: String tableName = StringUtils.toUpperEnglish(table.name);
297: if (up.startsWith(tableName)) {
298: if (best == null || tableName.length() > best.length()) {
299: best = tableName;
300: bestTable = table;
301: }
302: }
303: }
304: if (best == null) {
305: return null;
306: }
307: sentence.addTable(bestTable);
308: query = query.substring(best.length());
309: // while(query.length()>0 && Character.isWhitespace(query.charAt(0))) {
310: // query = query.substring(1);
311: // }
312: return query;
313: }
314:
315: public String matchColumnAlias(String query, Sentence sentence,
316: boolean add) {
317: String up = StringUtils.toUpperEnglish(query);
318: int i = 0;
319: if (query.indexOf(' ') < 0) {
320: return null;
321: }
322: for (; i < up.length(); i++) {
323: char ch = up.charAt(i);
324: if (ch != '_' && !Character.isLetterOrDigit(ch)) {
325: break;
326: }
327: }
328: if (i == 0) {
329: return null;
330: }
331: String alias = up.substring(0, i);
332: if (Parser.isKeyword(alias)) {
333: return null;
334: }
335: return query.substring(alias.length());
336: }
337:
338: public String matchTableAlias(String query, Sentence sentence,
339: boolean add) {
340: String up = StringUtils.toUpperEnglish(query);
341: int i = 0;
342: if (query.indexOf(' ') < 0) {
343: return null;
344: }
345: for (; i < up.length(); i++) {
346: char ch = up.charAt(i);
347: if (ch != '_' && !Character.isLetterOrDigit(ch)) {
348: break;
349: }
350: }
351: if (i == 0) {
352: return null;
353: }
354: String alias = up.substring(0, i);
355: if (Parser.isKeyword(alias)) {
356: return null;
357: }
358: if (add) {
359: sentence.addAlias(alias, sentence.getLastTable());
360: }
361: HashMap map = sentence.getAliases();
362: if ((map != null && map.containsKey(alias))
363: || (sentence.getLastTable() == null)) {
364: if (add && query.length() == alias.length()) {
365: return query;
366: }
367: query = query.substring(alias.length());
368: return query;
369: } else {
370: HashSet tables = sentence.getTables();
371: if (tables != null) {
372: String best = null;
373: for (Iterator it = tables.iterator(); it.hasNext();) {
374: DbTableOrView table = (DbTableOrView) it.next();
375: String tableName = StringUtils
376: .toUpperEnglish(table.name);
377: //DbTableOrView[] tables = contents.defaultSchema.tables;
378: //for(int i=0; i<tables.length; i++) {
379: // DbTableOrView table = tables[i];
380: // String tableName = StringUtils.toUpperEnglish(table.name);
381: if (alias.startsWith(tableName)
382: && (best == null || tableName.length() > best
383: .length())) {
384: best = tableName;
385: }
386: }
387: if (best != null) {
388: query = query.substring(best.length());
389: return query;
390: }
391: }
392: return null;
393: }
394: }
395:
396: public String matchColumn(String query, Sentence sentence) {
397: String up = StringUtils.toUpperEnglish(query);
398: HashSet set = sentence.getTables();
399: DbTableOrView[] tables = contents.defaultSchema.tables;
400: String best = null;
401: for (int i = 0; i < tables.length; i++) {
402: DbTableOrView table = tables[i];
403: if (set != null && !set.contains(table)) {
404: continue;
405: }
406: if (table == null || table.columns == null) {
407: continue;
408: }
409: for (int j = 0; j < table.columns.length; j++) {
410: String name = StringUtils
411: .toUpperEnglish(table.columns[j].name);
412: if (up.startsWith(name)) {
413: String b = query.substring(name.length());
414: if (best == null || b.length() < best.length()) {
415: best = b;
416: }
417: }
418: }
419: }
420: return best;
421: }
422: }
|