/* * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Version 1.0, and under the Eclipse Public License, Version 1.0 * (http://h2database.com/html/license.html). * Initial Developer: H2 Group */ package org.h2.server.web; import java.util.HashMap; import java.util.HashSet; import org.h2.bnf.BnfVisitor; import org.h2.bnf.Rule; import org.h2.bnf.RuleHead; import org.h2.bnf.Sentence; import org.h2.command.Parser; import org.h2.message.DbException; import org.h2.util.StringUtils; /** * A BNF terminal rule that is linked to the database context information. * This class is used by the H2 Console, to support auto-complete. */ public class DbContextRule implements Rule { static final int COLUMN = 0, TABLE = 1, TABLE_ALIAS = 2; static final int NEW_TABLE_ALIAS = 3; static final int COLUMN_ALIAS = 4, SCHEMA = 5; private DbContents contents; private int type; DbContextRule(DbContents contents, int type) { this.contents = contents; this.type = type; } public void setLinks(HashMap<String, RuleHead> ruleMap) { // nothing to do } public void accept(BnfVisitor visitor) { // nothing to do } public boolean autoComplete(Sentence sentence) { String query = sentence.getQuery(), s = query; String up = sentence.getQueryUpper(); switch (type) { case SCHEMA: { DbSchema[] schemas = contents.schemas; String best = null; DbSchema bestSchema = null; for (DbSchema schema: schemas) { String name = StringUtils.toUpperEnglish(schema.name); if (up.startsWith(name)) { if (best == null || name.length() > best.length()) { best = name; bestSchema = schema; } } else if (s.length() == 0 || name.startsWith(up)) { if (s.length() < name.length()) { sentence.add(name, name.substring(s.length()), type); sentence.add(schema.quotedName + ".", schema.quotedName.substring(s.length()) + ".", Sentence.CONTEXT); } } } if (best != null) { sentence.setLastMatchedSchema(bestSchema); s = s.substring(best.length()); } break; } case TABLE: { DbSchema schema = sentence.getLastMatchedSchema(); if (schema == null) { schema = contents.defaultSchema; } DbTableOrView[] tables = schema.tables; String best = null; DbTableOrView bestTable = null; for (DbTableOrView table : tables) { String compare = up; String name = StringUtils.toUpperEnglish(table.name); if (table.quotedName.length() > name.length()) { name = table.quotedName; compare = query; } if (compare.startsWith(name)) { if (best == null || name.length() > best.length()) { best = name; bestTable = table; } } else if (s.length() == 0 || name.startsWith(compare)) { if (s.length() < name.length()) { sentence.add(table.quotedName, table.quotedName.substring(s.length()), Sentence.CONTEXT); } } } if (best != null) { sentence.setLastMatchedTable(bestTable); sentence.addTable(bestTable); s = s.substring(best.length()); } break; } case NEW_TABLE_ALIAS: s = autoCompleteTableAlias(sentence, true); break; case TABLE_ALIAS: s = autoCompleteTableAlias(sentence, false); break; case COLUMN_ALIAS: { int i = 0; if (query.indexOf(' ') < 0) { break; } for (; i < up.length(); i++) { char ch = up.charAt(i); if (ch != '_' && !Character.isLetterOrDigit(ch)) { break; } } if (i == 0) { break; } String alias = up.substring(0, i); if (Parser.isKeyword(alias, true)) { break; } s = s.substring(alias.length()); break; } case COLUMN: { HashSet<DbTableOrView> set = sentence.getTables(); String best = null; DbTableOrView last = sentence.getLastMatchedTable(); if (last != null && last.columns != null) { for (DbColumn column : last.columns) { String compare = up; String name = StringUtils.toUpperEnglish(column.name); if (column.quotedName.length() > name.length()) { name = column.quotedName; compare = query; } if (compare.startsWith(name)) { String b = s.substring(name.length()); if (best == null || b.length() < best.length()) { best = b; } else if (s.length() == 0 || name.startsWith(compare)) { if (s.length() < name.length()) { sentence.add(column.name, column.name.substring(s.length()), Sentence.CONTEXT); } } } } } for (DbSchema schema : contents.schemas) { for (DbTableOrView table : schema.tables) { if (table != last && set != null && !set.contains(table)) { continue; } if (table == null || table.columns == null) { continue; } for (DbColumn column : table.columns) { String name = StringUtils.toUpperEnglish(column.name); if (up.startsWith(name)) { String b = s.substring(name.length()); if (best == null || b.length() < best.length()) { best = b; } } else if (s.length() == 0 || name.startsWith(up)) { if (s.length() < name.length()) { sentence.add(column.name, column.name.substring(s.length()), Sentence.CONTEXT); } } } } } if (best != null) { s = best; } break; } default: throw DbException.throwInternalError("type=" + type); } if (!s.equals(query)) { while (s.length() > 0 && Character.isSpaceChar(s.charAt(0))) { s = s.substring(1); } sentence.setQuery(s); return true; } return false; } private static String autoCompleteTableAlias(Sentence sentence, boolean newAlias) { String s = sentence.getQuery(); String up = sentence.getQueryUpper(); int i = 0; for (; i < up.length(); i++) { char ch = up.charAt(i); if (ch != '_' && !Character.isLetterOrDigit(ch)) { break; } } if (i == 0) { return s; } String alias = up.substring(0, i); if ("SET".equals(alias) || Parser.isKeyword(alias, true)) { return s; } if (newAlias) { sentence.addAlias(alias, sentence.getLastTable()); } HashMap<String, DbTableOrView> map = sentence.getAliases(); if ((map != null && map.containsKey(alias)) || (sentence.getLastTable() == null)) { if (newAlias && s.length() == alias.length()) { return s; } s = s.substring(alias.length()); if (s.length() == 0) { sentence.add(alias + ".", ".", Sentence.CONTEXT); } return s; } HashSet<DbTableOrView> tables = sentence.getTables(); if (tables != null) { String best = null; for (DbTableOrView table : tables) { String tableName = StringUtils.toUpperEnglish(table.name); if (alias.startsWith(tableName) && (best == null || tableName.length() > best.length())) { sentence.setLastMatchedTable(table); best = tableName; } else if (s.length() == 0 || tableName.startsWith(alias)) { sentence.add(tableName + ".", tableName.substring(s.length()) + ".", Sentence.CONTEXT); } } if (best != null) { s = s.substring(best.length()); if (s.length() == 0) { sentence.add(alias + ".", ".", Sentence.CONTEXT); } return s; } } return s; } }