From: SVN by r. <sv...@ca...> - 2009-02-27 08:10:59
|
Author: roy Date: 2009-02-27 09:10:51 +0100 (Fri, 27 Feb 2009) New Revision: 380 Added: src/main/java/nl/improved/sqlclient/util/SQLParser.java src/main/java/nl/improved/sqlclient/util/SQLPart.java Log: new sql parsing api Added: src/main/java/nl/improved/sqlclient/util/SQLParser.java =================================================================== --- src/main/java/nl/improved/sqlclient/util/SQLParser.java 2009-02-20 21:59:19 UTC (rev 379) +++ src/main/java/nl/improved/sqlclient/util/SQLParser.java 2009-02-27 08:10:51 UTC (rev 380) @@ -0,0 +1,396 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package nl.improved.sqlclient.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import nl.improved.sqlclient.Point; +import nl.improved.sqlclient.SQLCommand; +import nl.improved.sqlclient.TabCompletionInfo; + +/** + * + * @author roy + */ +public class SQLParser { + final static char[] breakCharacters = new char[]{' ', '\t', '.', ',', '(',')'}; + /** + * A regular expression statement for name chars. + */ + private static final String NAMING_CHAR = "(_|-|[0-9]|[A-Z])"; + /** + * A table name regular expression statement. + */ + private static final String TABLE = "[A-Z]+"+NAMING_CHAR+"*"; + /** + * A column name regular expression statement. + */ + private static final String COLUMN = "(\\*|[A-Z]+"+NAMING_CHAR+"*)"; + /** + * A variable (table name + columnname) regular expression statement. + */ + private static final String VAR = "(|"+TABLE+"\\.)"+COLUMN; + /** + * SQL Comparators. + */ + private static final String COMPARATOR = "(=|<>|<|>|LIKE)"; + /** + * A sql variable description. + */ + private static final String VALUE = "('.*'|[0-9]+|"+VAR+")"; + + public static TabCompletionInfo getTabCompletionInfo(SQLCommand command, Point commandPoint) { + List<? extends CharSequence> lines = command.getLines(); + return getTabCompletionInfo(lines, commandPoint); + } + public static TabCompletionInfo getTabCompletionInfo(List<? extends CharSequence> lines, Point commandPoint) { + StringBuffer buf = new StringBuffer(); + int position = 0; + for (int y = 0; y < lines.size(); y++) { + CharSequence seq = lines.get(y); + if (y < commandPoint.y) { + position+=seq.length(); + } + if (y == commandPoint.y) { + position+=commandPoint.x; + } + buf.append(seq); + } + return getTabCompletionInfo(buf, position); + } + + public static TabCompletionInfo getTabCompletionInfo(CharSequence sql, int position) { + + List<SQLPart> parts = parse(sql); + System.err.println("PARTS: "+ parts); + int charsLeft = position; + for (SQLPart part : parts) { + if (charsLeft <= part.getPart().length()) { + switch (part.getSQLType()) { + case SELECT : + return getSelectTabCompletionInfo(part, parts, charsLeft); + case FROM : + return getFromTabCompletionInfo(part, parts, charsLeft); + case WHERE : + return getWhereTabCompletionInfo(part, parts, charsLeft); + case GROUPBY : + return getGroupByTabCompletionInfo(part, parts, charsLeft); + case ORDERBY : + return getOrderByTabCompletionInfo(part, parts, charsLeft); + case DELETE : + return getDeleteTabCompletionInfo(part, parts, charsLeft); + case UPDATE : + return getUpdateTabCompletionInfo(part, parts, charsLeft); + case SET : + return getSetTabCompletionInfo(part, parts, charsLeft); + } + } else { + charsLeft -=part.getPart().length(); + } + } + return new TabCompletionInfo(TabCompletionInfo.MatchType.SQL_KEYWORD + , Arrays.asList(new String[]{"SELECT", "INSERT INTO", "UPDATE", "DELETE FROM", "ALTER TABLE"}) + , sql); + } + + + static List<SQLPart> parse(CharSequence sqlSequence) { + List<SQLPart> returnValue = new ArrayList<SQLPart>(); + if (sqlSequence.length() == 0) { + return returnValue; + } + SQLPart.SQLType sqlType; + int start = 0; + int end = sqlSequence.length(); + String sql = sqlSequence.toString().toUpperCase(); + //System.out.println("SQL: "+ sql); + //System.out.println("MATCHES: "+ sql.matches("SELECT.*")); + if (sql.matches("[\\s]*SELECT.*")) { + sqlType = SQLPart.SQLType.SELECT; + if (sql.matches("[\\s]*SELECT.*FROM.*")) { + end = sql.indexOf("FROM"); + } else { + end = sqlSequence.length(); + } + } else if (sql.matches("[\\s]*FROM.*")) { + sqlType = SQLPart.SQLType.FROM; + if (sql.matches("[\\s]*FROM.*WHERE.*")) { + end = sql.indexOf("WHERE"); + } + } else if (sql.matches("[\\s]*WHERE.*")) { + sqlType = SQLPart.SQLType.WHERE; + if (sql.matches("[\\s]*WHERE.*ORDER BY.*")) { + end = sql.indexOf(" ORDER BY"); + } else if (sql.matches("[\\s]*WHERE.*GROUP BY.*")) { + end = sql.indexOf("GROUP BY"); + } + } else if (sql.matches("[\\s]*ORDER BY.*")) { + sqlType = SQLPart.SQLType.ORDERBY; + if (sql.matches("[\\s]*ORDER BY.*GROUP BY.*")) { + end = sql.indexOf("GROUP BY"); + } + } else if (sql.matches("[\\s]*GROUP BY.*")) { + sqlType = SQLPart.SQLType.GROUPBY; + } else if (sql.matches("[\\s]*DELETE.*")) { + sqlType = SQLPart.SQLType.DELETE; + if (sql.matches("[\\s]*DELETE FROM.*")) { + end = sql.indexOf("FROM"); + } else { + end = sqlSequence.length(); + } + } else if (sql.matches("[\\s]*UPDATE.*")) { + sqlType = SQLPart.SQLType.UPDATE; + if (sql.matches("[\\s]*UPDATE.*SET.*")) { + end = sql.indexOf("SET"); + } else { + end = sqlSequence.length(); + } + } else if (sql.matches("[\\s]*SET.*")) { + sqlType = SQLPart.SQLType.SET; + if (sql.matches("[\\s]*WHERE.*")) { + end = sql.indexOf("WHERE"); + } else { + end = sqlSequence.length(); + } + } else { + sqlType = SQLPart.SQLType.OTHER; + } + returnValue.add(new SQLPart(sqlType, sqlSequence.subSequence(start, end))); + returnValue.addAll(parse(sqlSequence.subSequence(end, sqlSequence.length()))); + return returnValue; + } + + private static TabCompletionInfo getDeleteTabCompletionInfo(SQLPart part, List<SQLPart> parts, int charsLeft) { + throw new UnsupportedOperationException("Not yet implemented"); + } + + private static TabCompletionInfo getFromTabCompletionInfo(SQLPart part, List<SQLPart> parts, int charsLeft) { + String stringPart = part.getPart().toString(); + String subPart = stringPart.substring(stringPart.toUpperCase().indexOf("FROM") +"FROM".length(), charsLeft); + String end; + if (subPart.indexOf(',') > 0) { + end = subPart.substring(subPart.lastIndexOf(',')+1); + } else { + end = subPart; + } + String WHERE = "W|WH|WHE|WHER|WHERE"; + String GROUP_BY = "G|GR|GRO|GROU|GROUP|GROUP |GROUP B|GROUP BY"; + String ORDER_BY = "O|OR|ORD|ORDE|ORDER|ORDER |ORDER B|ORDER BY"; + if (subPart.trim().length() > 0 && (end.endsWith(" ") || subPart.matches(".*"+TABLE+"[\\s]+("+WHERE+"|"+GROUP_BY+"|"+ORDER_BY+")")) && + !subPart.trim().endsWith(",")) { + if (subPart.lastIndexOf(" ") >0) { + end = end.substring(end.lastIndexOf(' ')+1); + } + return new TabCompletionInfo(TabCompletionInfo.MatchType.SQL_KEYWORD, Arrays.asList(new String[]{"WHERE", "GROUP BY", "ORDER BY", ","}), end.trim()); + } + return new TabCompletionInfo(TabCompletionInfo.MatchType.TABLE_NAMES, Arrays.asList(new String[]{}), end.trim()); + } + + private static TabCompletionInfo getGroupByTabCompletionInfo(SQLPart part, List<SQLPart> parts, int charsLeft) { + String stringPart = part.getPart().toString(); + String subPart = stringPart.substring(stringPart.toUpperCase().indexOf("GROUP BY")+"GROUP BY".length(), charsLeft); + List<String> possibleMatches = new ArrayList<String>(); + for (SQLPart pPart : parts) { + if (pPart.getSQLType() == SQLPart.SQLType.SELECT) { + String pSubPart = pPart.getPart().toString().substring("SELECT".length()).trim(); + int lastIndex = 0; + int newIndex; + while ( (newIndex = pSubPart.indexOf(',', lastIndex)) > 0) { + String colName = pSubPart.substring(lastIndex, newIndex).trim(); + if (colName.indexOf(' ') >=0) { + colName = colName.substring(colName.lastIndexOf(' ')); + } + if (!possibleMatches.contains(colName)) { + possibleMatches.add(colName); + } + lastIndex = newIndex+1; + } + String colName = pSubPart.substring(lastIndex).trim(); + if (colName.indexOf(' ') >=0) { // alias + colName = colName.substring(colName.lastIndexOf(' ')); + } + if (!possibleMatches.contains(colName) && colName.length() > 0) { + possibleMatches.add(colName); + } + } + } + if (subPart.trim().length() == 0 || subPart.trim().endsWith(",")) { + return new TabCompletionInfo(TabCompletionInfo.MatchType.OTHER, possibleMatches); + } + String end; + if (subPart.indexOf(',') > 0) { + end = subPart.substring(subPart.lastIndexOf(',')).trim(); + } else { + end = subPart.trim(); + } + return new TabCompletionInfo(TabCompletionInfo.MatchType.OTHER, possibleMatches, end); + } + + private static TabCompletionInfo getOrderByTabCompletionInfo(SQLPart part, List<SQLPart> parts, int charsLeft) { + String stringPart = part.getPart().toString(); + String subPart = stringPart.substring(stringPart.toUpperCase().indexOf("ORDER BY")+"ORDER BY".length(), charsLeft); + List<String> possibleMatches = new ArrayList<String>(); + for (SQLPart pPart : parts) { + } + if (subPart.trim().length() == 0 || subPart.trim().endsWith(",")) { + return new TabCompletionInfo(TabCompletionInfo.MatchType.OTHER, possibleMatches); + } + String end; + if (subPart.indexOf(',') > 0) { + end = subPart.substring(subPart.lastIndexOf(',')).trim(); + } else { + end = subPart.trim(); + } + return new TabCompletionInfo(TabCompletionInfo.MatchType.OTHER, possibleMatches, end); + } + + private static TabCompletionInfo getSelectTabCompletionInfo(SQLPart part, List<SQLPart> parts, int position) { + // if it looks like: + // SELECT x + // return "FROM" + String startOfCommand = part.getPart().toString(); + String upCommand = startOfCommand.toUpperCase(); + if (upCommand.substring(upCommand.indexOf("SELECT") + "SELECT".length()) + .matches("[\\s]+"+VAR+"(|[\\s]*,[\\s]*"+VAR+"[\\s]*)[\\s]+(|F|FR|FRO|FROM)")) { + String end = startOfCommand.substring(startOfCommand.lastIndexOf(' ')+1); + return new TabCompletionInfo(TabCompletionInfo.MatchType.SQL_KEYWORD + , Arrays.asList(new String[]{"FROM"}), end); + } + // if it looks like: + // SELECT + // or + // SELECT A, + // return matching columnnames when table names are provided in from clause + // other wise return table names + // TODO check for WHERE Clause + String lastKeyword = "SELECT"; + String sub = startOfCommand.substring(upCommand.indexOf(lastKeyword)+lastKeyword.length()).trim(); + String tableName; + if (sub.indexOf(',') > 0) { + tableName = sub.substring(sub.lastIndexOf(',')+1).trim(); + } else { + tableName = sub; + } + if (tableName.indexOf('.') > 0) { + return new TabCompletionInfo(TabCompletionInfo.MatchType.COLUMN_NAMES + , Arrays.asList(new String[]{tableName.substring(0,tableName.indexOf('.'))}) + , tableName.substring(tableName.indexOf('.')+1)); + } + return new TabCompletionInfo(TabCompletionInfo.MatchType.TABLE_NAMES + , Arrays.asList(new String[0]), tableName); + } + + private static TabCompletionInfo getSetTabCompletionInfo(SQLPart part, List<SQLPart> parts, int charsLeft) { + String startOfCommand = part.getPart().toString().substring(0, charsLeft); + String tmpCommand = startOfCommand.substring(startOfCommand.toUpperCase().indexOf("SET")+"SET".length()+1); + if (tmpCommand.indexOf(',') >0) { + tmpCommand = tmpCommand.substring(tmpCommand.lastIndexOf(',')+1); + } + if (tmpCommand.endsWith(" ")) { + if (tmpCommand.matches(".*"+COMPARATOR+"[\\s]*")) { + return new TabCompletionInfo(TabCompletionInfo.MatchType.COLUMN_NAMES + , getTableNames(parts)); + } + if (tmpCommand.matches(".*"+COMPARATOR+"[\\s]*"+VAR+"[\\s]*")) { + return new TabCompletionInfo(TabCompletionInfo.MatchType.SQL_KEYWORD + , Arrays.asList(new String[]{",", "WHERE"})); + } + if (tmpCommand.trim().length() > 0) { + return new TabCompletionInfo(TabCompletionInfo.MatchType.SQL_KEYWORD + , Arrays.asList(new String[]{"=", "<>", "LIKE", "<", ">"})); + } + } + if (tmpCommand.trim().length() == 0) { + return new TabCompletionInfo(TabCompletionInfo.MatchType.COLUMN_NAMES + , getTableNames(parts)); + } + String end; + if (tmpCommand.indexOf(' ') > 0) { + end = tmpCommand.substring(tmpCommand.lastIndexOf(' ')+1).trim(); + } else { + end = tmpCommand.trim(); + } + if (tmpCommand.trim().length() > 0) { + return new TabCompletionInfo(TabCompletionInfo.MatchType.SQL_KEYWORD + , Arrays.asList(new String[]{"=", "<>", "LIKE", "<", ">"}), end); + } + return new TabCompletionInfo(TabCompletionInfo.MatchType.SQL_KEYWORD + , Arrays.asList(new String[]{",", "WHERE"})); // TODO END + } + + private static List<String> getTableNames(List<SQLPart> parts) { + SQLPart fromSqlPart = null; + for (SQLPart tmpPart : parts) { + if (tmpPart.getSQLType() == SQLPart.SQLType.FROM || tmpPart.getSQLType() == SQLPart.SQLType.UPDATE) { + fromSqlPart = tmpPart; + } + } + return fromSqlPart.getTableNames(); + } + + private static TabCompletionInfo getUpdateTabCompletionInfo(SQLPart part, List<SQLPart> parts, int charsLeft) { + String startOfCommand = part.getPart().toString().substring(0, charsLeft); + String tmpCommand = startOfCommand.substring(startOfCommand.toUpperCase().indexOf("UPDATE")+"UPDATE".length()+1); + if (tmpCommand.trim().length() > 0 && tmpCommand.lastIndexOf(' ') > 0) { + String end = tmpCommand.substring(tmpCommand.lastIndexOf(' ') +1); + return new TabCompletionInfo(TabCompletionInfo.MatchType.SQL_KEYWORD + , Arrays.asList(new String[]{"SET"}), end); + } + return new TabCompletionInfo(TabCompletionInfo.MatchType.TABLE_NAMES + , new ArrayList(), tmpCommand.trim()); + } + + private static TabCompletionInfo getWhereTabCompletionInfo(SQLPart part, List<SQLPart> parts, int charsLeft) { + // find out if it looks like + // 'SELECT x FROM A,B WHERE' + // or + // 'SELECT x FROM A,B WHERE A.x='x' AND/OR ' + String startOfCommand = part.getPart().toString().substring(0, charsLeft); + String tmpCommand = startOfCommand.substring(startOfCommand.toUpperCase().indexOf("WHERE")+"WHERE".length()+1); + String upperCommandString = tmpCommand.toUpperCase(); + //String VAR = "(|"+TABLE+"\\.)(|"+COLUMN+")"; + //String VALUE = "('.*'|[0-9]+|"+VAR+")"; + // VALUE COMPERATOR VALUE + // VALUE IN (.*) + String regExp = "(|"+VALUE+"(|[\\s]*"+COMPARATOR+"[\\s]*(|"+VALUE+"(|([\\s]+(AND|OR)[\\s]+(|"+VALUE+"(|[\\s]*"+COMPARATOR+"[\\s]*(|"+VALUE+"))))*))|[\\s]*IN[\\s]*(.*)[\\s]*))"; + if (upperCommandString.matches(regExp) || upperCommandString.endsWith(".")) { + System.err.println("****** MATCHES: "+ upperCommandString); + int lastIndex = tmpCommand.lastIndexOf(' '); + lastIndex = Math.max(lastIndex, tmpCommand.lastIndexOf('\t')); + lastIndex = Math.max(lastIndex, tmpCommand.lastIndexOf('=')); + lastIndex = Math.max(lastIndex, tmpCommand.lastIndexOf('>')); + lastIndex = Math.max(lastIndex, tmpCommand.lastIndexOf('>')); + int lastBreakIndex = lastIndex; + lastIndex = Math.max(lastIndex, tmpCommand.lastIndexOf('.')); + String end = tmpCommand.substring(lastIndex+1); + if (lastBreakIndex < 0) { + lastBreakIndex = 0; + } + List tableNames = (lastIndex > lastBreakIndex) && lastBreakIndex >=0 ? + Arrays.asList(new String[]{tmpCommand.substring(lastBreakIndex,lastIndex).trim()}) : + getTableNames(parts); + return new TabCompletionInfo(TabCompletionInfo.MatchType.COLUMN_NAMES + , tableNames, end); + } + System.err.println("****** NOT MATCHES: "+ upperCommandString+"\n"+ regExp); + //else System.out.println("'"+upperCommandString +"'\n not matches\n"+regExp); + String end; + if (upperCommandString.matches(".*[\\s]+")) { + end = ""; + } else { + int lastIndex = tmpCommand.lastIndexOf(' '); + lastIndex = Math.max(lastIndex, tmpCommand.lastIndexOf('\t')); + lastIndex = Math.max(lastIndex, tmpCommand.lastIndexOf('=')); + lastIndex = Math.max(lastIndex, tmpCommand.lastIndexOf('>')); + lastIndex = Math.max(lastIndex, tmpCommand.lastIndexOf('>')); + end = tmpCommand.substring(lastIndex+1); + } + return new TabCompletionInfo(TabCompletionInfo.MatchType.SQL_KEYWORD + , Arrays.asList(new String[]{"AND", "OR", "IN", "GROUP BY", "ORDER BY"}), end); + } + +} Added: src/main/java/nl/improved/sqlclient/util/SQLPart.java =================================================================== --- src/main/java/nl/improved/sqlclient/util/SQLPart.java 2009-02-20 21:59:19 UTC (rev 379) +++ src/main/java/nl/improved/sqlclient/util/SQLPart.java 2009-02-27 08:10:51 UTC (rev 380) @@ -0,0 +1,84 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package nl.improved.sqlclient.util; + +import java.util.ArrayList; +import java.util.List; +import nl.improved.sqlclient.TabCompletionInfo; + +/** + * + * @author roy + */ +public class SQLPart { + + enum SQLType {SELECT, INSERT, UPDATE, DELETE, WHERE, VALUES, FROM, GROUPBY, ORDERBY, OTHER, SET} + + private SQLType sqlType; + private CharSequence part; + + public SQLPart(SQLType sqlType, CharSequence part) { + this.sqlType = sqlType; + this.part = part; + } + + public SQLType getSQLType() { + return sqlType; + } + + public CharSequence getPart() { + return part; + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof SQLPart)) { + return false; + } + SQLPart other = (SQLPart) obj; + return other.part.equals(part) && other.sqlType == sqlType; + } + + @Override + public String toString() { + return sqlType+": "+ part.toString(); + } + + public List<String> getTableNames() { + if (sqlType == SQLType.FROM || sqlType == SQLType.UPDATE) { + List<String> returnValue = new ArrayList<String>(); + String stringPart = getPart().toString(); + String sub; + if (sqlType == SQLType.FROM) { + sub = stringPart.substring(stringPart.toUpperCase().indexOf("FROM")+"FROM".length()); + } else { + sub = stringPart.substring(stringPart.toUpperCase().indexOf("UPDATE")+"UPDATE".length()); + } + int lastIndex = 0; + int newIndex; + while ( (newIndex = sub.indexOf(',', lastIndex)) > 0) { + String tableName = sub.substring(lastIndex, newIndex).trim(); + if (tableName.indexOf(" ") > 0) { + tableName = tableName.substring(0, tableName.indexOf(" ")).trim(); + } + System.err.println("********ADDTN: "+ tableName); + returnValue.add(tableName); + lastIndex = newIndex+1; + } + String tmp = sub.substring(lastIndex).trim(); + if (tmp.length() > 0) { + if (tmp.indexOf(" ") > 0) { + tmp = tmp.substring(0, tmp.indexOf(" ")).trim(); + } + System.err.println("********ADD: "+ tmp); + returnValue.add(tmp); + } + return returnValue; + } + throw new IllegalStateException("Table names are only available for FROM,UPDATE ("+ sqlType+")"); + } + +} \ No newline at end of file |