[Squirrel-sql-commits] sql12/app/src/net/sourceforge/squirrel_sql/client/session/schemainfo CaseInse
A Java SQL client for any JDBC compliant database
Brought to you by:
colbell,
gerdwagner
From: Gerd W. <ger...@us...> - 2006-05-03 10:49:50
|
Update of /cvsroot/squirrel-sql/sql12/app/src/net/sourceforge/squirrel_sql/client/session/schemainfo In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv22747/app/src/net/sourceforge/squirrel_sql/client/session/schemainfo Added Files: CaseInsensitiveString.java DefaultKeywords.java I18NStrings.properties SchemaInfo.java SchemaInfoUpdateCheck.java SchemaInfoUpdateListener.java Log Message: Refactoring and fixed bugs in error and syntax highlighting --- NEW FILE: CaseInsensitiveString.java --- package net.sourceforge.squirrel_sql.client.session.schemainfo; /** * This class was introduced to allow the Syntax plugin * to find out if a token is a column, table, etc without * creating new String objects. * Since syntax highlightning needs a lot of those checks * the usage of this class leads to better performance and * memory finger print. */ public class CaseInsensitiveString implements Comparable { private char[] value = new char[0]; private int offset = 0; private int count = 0; private int hash = 0; private boolean _isMutable; public CaseInsensitiveString(String s) { value = new char[s.length()]; s.getChars(0, s.length(), value, 0); offset = 0; count = s.length(); hash = 0; _isMutable = false; } public CaseInsensitiveString() { _isMutable = true; } public void setCharBuffer(char[] buffer, int beginIndex, int len) { if(false == _isMutable) { throw new UnsupportedOperationException("This CaseInsensitiveString is immutable"); } value = buffer; offset = beginIndex; count = len; hash = 0; } public int hashCode() { int h = hash; if (h == 0) { int off = offset; char val[] = value; int len = count; for (int i = 0; i < len; i++) { h = 31 * h + Character.toUpperCase(val[off++]); } hash = h; } return h; } public boolean equals(Object obj) { if(obj instanceof String) { String other = (String) obj; if(other.length() != count) { return false; } for(int i=0; i < count; ++i) { char c1 = value[offset + i]; char c2 = other.charAt(i); // If characters don't match but case may be ignored, // try converting both characters to uppercase. // If the results match, then the comparison scan should // continue. char u1 = Character.toUpperCase(c1); char u2 = Character.toUpperCase(c2); if (u1 == u2) { continue; } // Unfortunately, conversion to uppercase does not work properly // for the Georgian alphabet, which has strange rules about case // conversion. So we need to make one last check before // exiting. if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { continue; } return false; } return true; } else if(obj instanceof CaseInsensitiveString) { CaseInsensitiveString other = (CaseInsensitiveString) obj; if(other.count != count) { return false; } for(int i=0; i < count; ++i) { char c1 = value[offset + i]; char c2 = other.value[other.offset + i]; // If characters don't match but case may be ignored, // try converting both characters to uppercase. // If the results match, then the comparison scan should // continue. char u1 = Character.toUpperCase(c1); char u2 = Character.toUpperCase(c2); if (u1 == u2) { continue; } // Unfortunately, conversion to uppercase does not work properly // for the Georgian alphabet, which has strange rules about case // conversion. So we need to make one last check before // exiting. if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { continue; } return false; } return true; } else { return false; } } public String toString() { return new String(value, offset, count); } public int compareTo(Object o) { // return this.toString().toLowerCase().compareTo(o.toString().toLowerCase()); CaseInsensitiveString anotherString = (CaseInsensitiveString) o; int len1 = count; int len2 = anotherString.count; int n = Math.min(len1, len2); char v1[] = value; char v2[] = anotherString.value; int i = offset; int j = anotherString.offset; if (i == j) { int k = i; int lim = n + i; while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (Character.toLowerCase(c1) != Character.toLowerCase(c2)) { return Character.toLowerCase(c1) - Character.toLowerCase(c2); } k++; } } else { while (n-- != 0) { char c1 = v1[i++]; char c2 = v2[j++]; if (Character.toLowerCase(c1) != Character.toLowerCase(c2)) { return Character.toLowerCase(c1) - Character.toLowerCase(c2); } } } return len1 - len2; } } --- NEW FILE: I18NStrings.properties --- SchemaInfo.UnableToLoadColumns=Unable to load columns for table {0} because database meta data is unavailable. Propably reconnecting failed. SchemaInfo.SuccessfullyRestoredDatabaseMetaData=Successfully restored database meta data SchemaInfo.loadingCatalogs=Loading catalogs SchemaInfo.loadingDataTypes=Loading data types SchemaInfo.loadingFunctions=Loading functions SchemaInfo.loadingKeywords=Loading keywords SchemaInfo.loadingSchemas=Loading schemas SchemaInfo.loadingStoredProcedures=Loading stored procedures SchemaInfo.loadingTables=Loading tables --- NEW FILE: DefaultKeywords.java --- package net.sourceforge.squirrel_sql.client.session.schemainfo; public interface DefaultKeywords { public static final String[] KEY_WORDS = new String[] { "ABSOLUTE", "ACTION", "ADD", "ALL", "ALTER", "AND", "AS", "ASC", "ASSERTION", "AUTHORIZATION", "AVG", "BETWEEN", "BY", "CASCADE", "CASCADED", "CASE", "CATALOG", "CHARACTER", "CHECK", "COLLATE", "COLLATION", "COLUMN", "COMMIT", "COMMITTED", "CONNECT", "CONNECTION", "CONSTRAINT", "COUNT", "CORRESPONDING", "CREATE", "CROSS", "CURRENT", "CURSOR", "DECLARE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DIAGNOSTICS", "DISCONNECT", "DISTINCT", "DOMAIN", "DROP", "ELSE", "END", "ESCAPE", "EXCEPT", "EXISTS", "EXTERNAL", "FALSE", "FETCH", "FIRST", "FOREIGN", "FROM", "FULL", "GET", "GLOBAL", "GRANT", "GROUP", "HAVING", "IDENTITY", "IMMEDIATE", "IN", "INITIALLY", "INNER", "INSENSITIVE", "INSERT", "INTERSECT", "INTO", "IS", "ISOLATION", "JOIN", "KEY", "LAST", "LEFT", "LEVEL", "LIKE", "LOCAL", "MATCH", "MAX", "MIN", "NAMES", "NEXT", "NO", "NOT", "NULL", "OF", "ON", "ONLY", "OPEN", "OPTION", "OR", "ORDER", "OUTER", "OVERLAPS", "PARTIAL", "PRESERVE", "PRIMARY", "PRIOR", "PRIVILIGES", "PUBLIC", "READ", "REFERENCES", "RELATIVE", "REPEATABLE", "RESTRICT", "REVOKE", "RIGHT", "ROLLBACK", "ROWS", "SCHEMA", "SCROLL", "SELECT", "SERIALIZABLE", "SESSION", "SET", "SIZE", "SOME", "SUM", "TABLE", "TEMPORARY", "THEN", "TIME", "TO", "TRANSACTION", "TRIGGER", "TRUE", "UNCOMMITTED", "UNION", "UNIQUE", "UNKNOWN", "UPDATE", "USAGE", "USER", "USING", "VALUES", "VIEW", "WHEN", "WHERE", "WITH", "WORK", "WRITE", "ZONE", "INDEX" }; } --- NEW FILE: SchemaInfo.java --- package net.sourceforge.squirrel_sql.client.session.schemainfo; /* * Copyright (C) 2001-2003 Colin Bell * co...@us... * * Copyright (C) 2001 Johan Compagner * jco...@j-... * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public [...1306 lines suppressed...] final HashMap cachedTableTypes = new HashMap(); final TreeMap keywords = new TreeMap(); final TreeMap dataTypes = new TreeMap(); final Map functions = Collections.synchronizedMap(new TreeMap()); final Map tableNames = Collections.synchronizedMap(new TreeMap()); final Map columnNames = Collections.synchronizedMap(new TreeMap()); final Map procedureNames = Collections.synchronizedMap(new TreeMap()); final Map extendedColumnInfosByTableName = Collections.synchronizedMap(new TreeMap()); final Map iTableInfos = Collections.synchronizedMap(new TreeMap()); final Map iProcedureInfos = Collections.synchronizedMap(new TreeMap()); final Hashtable tableInfosBySimpleName = new Hashtable(); final Hashtable procedureInfosBySimpleName = new Hashtable(); } } --- NEW FILE: SchemaInfoUpdateListener.java --- package net.sourceforge.squirrel_sql.client.session.schemainfo; public interface SchemaInfoUpdateListener { void schemaInfoUpdated(); } --- NEW FILE: SchemaInfoUpdateCheck.java --- package net.sourceforge.squirrel_sql.client.session.schemainfo; import net.sourceforge.squirrel_sql.fw.sql.*; import net.sourceforge.squirrel_sql.client.gui.session.BaseSessionInternalFrame; import net.sourceforge.squirrel_sql.client.gui.session.SQLInternalFrame; import net.sourceforge.squirrel_sql.client.gui.session.SessionInternalFrame; import net.sourceforge.squirrel_sql.client.session.ISession; import net.sourceforge.squirrel_sql.client.session.SQLExecutionInfo; import net.sourceforge.squirrel_sql.client.session.ISQLEntryPanel; import javax.swing.*; import java.util.HashMap; import java.util.Iterator; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.sql.DatabaseMetaData; /** * This class tries to update SchemaInfo after standard CREATE/ALTER statements. * This way Syntax higlightning and code completion are available just after * CREATE/ALTER statements were send to the DB. */ public class SchemaInfoUpdateCheck { private static final Pattern PATTERN_CREATE_TABLE = Pattern.compile("CREATE\\s+TABLE\\s+([A-Z0-9_\\.\"]+)"); private static final Pattern PATTERN_ALTER_TABLE = Pattern.compile("ALTER\\s+TABLE\\s+([A-Z0-9_\\.\"]+)"); private static final Pattern PATTERN_INSERT_INTO = Pattern.compile("SELECT\\s+INTO\\s+([A-Z0-9_\\.\"]+)"); private static final Pattern PATTERN_CREATE_VIEW = Pattern.compile("CREATE\\s+VIEW\\s+([A-Z0-9_\\.\"]+)"); private static final Pattern PATTERN_ALTER_VIEW = Pattern.compile("ALTER\\s+VIEW\\s+([A-Z0-9_\\.\"]+)"); private static final Pattern PATTERN_CREATE_PROCEDURE = Pattern.compile("CREATE\\s+PROCEDURE\\s+([A-Z0-9_\\.\"]+)"); private static final Pattern PATTERN_ALTER_PROCEDURE = Pattern.compile("ALTER\\s+PROCEDURE\\s+([A-Z0-9_\\.\"]+)"); private static final Pattern PATTERN_CREATE_FUNCTION = Pattern.compile("CREATE\\s+FUNCTION\\s+([A-Z0-9_\\.\"]+)"); private static final Pattern PATTERN_ALTER_FUNCTION = Pattern.compile("ALTER\\s+FUNCTION\\s+([A-Z0-9_\\.\"]+)"); private static final Pattern PATTERN_DROP_TABLE = Pattern.compile("DROP\\s+TABLE\\s+([A-Z0-9_\\.\"]+)"); private static final Pattern PATTERN_DROP_VIEW = Pattern.compile("DROP\\s+VIEW\\s+([A-Z0-9_\\.\"]+)"); private static final Pattern PATTERN_DROP_PROCEDURE = Pattern.compile("DROP\\s+PROCEDURE\\s+([A-Z0-9_\\.\"]+)"); private static final Pattern PATTERN_DROP_FUNCTION = Pattern.compile("DROP\\s+FUNCTION\\s+([A-Z0-9_\\.\"]+)"); private HashMap _updateDatabaseObjectInfos = new HashMap(); private HashMap _dropTableSimpleNames = new HashMap(); private HashMap _dropProcedureSimpleNames = new HashMap(); private ISession _session; private SQLDatabaseMetaData _dmd; public SchemaInfoUpdateCheck(ISession session) { _session = session; _dmd = _session.getSQLConnection().getSQLMetaData(); } public void addExecutionInfo(SQLExecutionInfo exInfo) { if(null == exInfo || null == exInfo.getSQL()) { return; } TableInfo[] tis = getTableInfos(exInfo.getSQL()); for (int i = 0; i < tis.length; i++) { _updateDatabaseObjectInfos.put(tis[i], tis[i]); } ProcedureInfo[] pi = getProcedureInfos(exInfo.getSQL()); for (int i = 0; i < pi.length; i++) { _updateDatabaseObjectInfos.put(pi[i], pi[i]); } String dtsn = getDropTableSimpleName(exInfo.getSQL()); if(null != dtsn) { _dropTableSimpleNames.put(dtsn, dtsn); } String dpsn = getDropProcedureSimpleName(exInfo.getSQL()); if(null != dpsn) { _dropProcedureSimpleNames.put(dpsn, dpsn); } } private String getDropProcedureSimpleName(String sql) { sql = sql.trim(); String upperSql = sql.toUpperCase(); Matcher matcher; matcher = PATTERN_DROP_PROCEDURE.matcher(upperSql); if(matcher.find()) { return createProcdureInfos(matcher, sql, true)[0].getSimpleName(); } matcher = PATTERN_DROP_FUNCTION.matcher(upperSql); if(matcher.find()) { return createProcdureInfos(matcher, sql, true)[0].getSimpleName(); } return null; } private String getDropTableSimpleName(String sql) { sql = sql.trim(); String upperSql = sql.toUpperCase(); Matcher matcher; matcher = PATTERN_DROP_TABLE.matcher(upperSql); if(matcher.find()) { return createTableInfos(matcher, sql, "TABLE", true)[0].getSimpleName(); } matcher = PATTERN_DROP_VIEW.matcher(upperSql); if(matcher.find()) { return createTableInfos(matcher, sql, "VIEW", true)[0].getSimpleName(); } return null; } public void flush() { if(60 < _updateDatabaseObjectInfos.size() + _dropTableSimpleNames.size() + _dropProcedureSimpleNames.size()) { // reload complete SchemaInfo SQLDatabaseMetaData dmd = _session.getSQLConnection().getSQLMetaData(); DatabaseObjectInfo sessionOI = new DatabaseObjectInfo(null, null, "SessionDummy", DatabaseObjectType.SESSION, dmd); _session.getSchemaInfo().reloadCache(sessionOI); } else { for(Iterator i = _updateDatabaseObjectInfos.keySet().iterator(); i.hasNext();) { IDatabaseObjectInfo doi = (IDatabaseObjectInfo) i.next(); _session.getSchemaInfo().reloadCache(doi); } for(Iterator i = _dropTableSimpleNames.keySet().iterator(); i.hasNext();) { String simpleTableName = (String) i.next(); _session.getSchemaInfo().cleanAndReloadCacheForSimpleTableName(simpleTableName); } for(Iterator i = _dropProcedureSimpleNames.keySet().iterator(); i.hasNext();) { String simpleProcName = (String) i.next(); _session.getSchemaInfo().cleanAndReloadCacheForSimpleProcedureName(simpleProcName); } } if(0 < _updateDatabaseObjectInfos.size() + _dropTableSimpleNames.size() + _dropProcedureSimpleNames.size()) { SwingUtilities.invokeLater(new Runnable() { public void run() { repaintSqlEditor(); } }); } } private void repaintSqlEditor() { BaseSessionInternalFrame activeSessionWindow = _session.getActiveSessionWindow(); if (activeSessionWindow instanceof SQLInternalFrame) { ISQLEntryPanel sqlEntryPanel = ((SQLInternalFrame) activeSessionWindow).getSQLPanelAPI().getSQLEntryPanel(); sqlEntryPanel.getTextComponent().repaint(); _session.getParserEventsProcessor(sqlEntryPanel.getIdentifier()).triggerParser(); } if (activeSessionWindow instanceof SessionInternalFrame) { ISQLEntryPanel sqlEntryPanel = ((SessionInternalFrame) activeSessionWindow).getSQLPanelAPI().getSQLEntryPanel(); sqlEntryPanel.getTextComponent().repaint(); _session.getParserEventsProcessor(sqlEntryPanel.getIdentifier()).triggerParser(); } } private ProcedureInfo[] getProcedureInfos(String sql) { sql = sql.trim(); String upperSql = sql.toUpperCase(); Matcher matcher; matcher = PATTERN_CREATE_PROCEDURE.matcher(upperSql); if(matcher.find()) { return createProcdureInfos(matcher, sql, false); } matcher = PATTERN_ALTER_PROCEDURE.matcher(upperSql); if(matcher.find()) { return createProcdureInfos(matcher, sql, true); } matcher = PATTERN_CREATE_FUNCTION.matcher(upperSql); if(matcher.find()) { return createProcdureInfos(matcher, sql, false); } matcher = PATTERN_ALTER_FUNCTION.matcher(upperSql); if(matcher.find()) { return createProcdureInfos(matcher, sql, true); } return new ProcedureInfo[0]; } private ProcedureInfo[] createProcdureInfos(Matcher matcher, String sql, boolean isAlterOrDrop) { int endIx = matcher.end(1); int len = matcher.group(1).length(); String proc = sql.substring(endIx - len, endIx); String[] splits = proc.split("\\."); String simpleName = splits[splits.length - 1]; simpleName = removeQuotes(simpleName); if(isAlterOrDrop) { String buf = _session.getSchemaInfo().getCaseSensitiveProcedureName(simpleName); if(null != buf) { simpleName = buf; } return new ProcedureInfo[]{new ProcedureInfo(null, null, simpleName, null, DatabaseMetaData.procedureResultUnknown, _dmd)}; } else { // DB2 stores all names in upper case. // PostgreSQL stores all names in lower case. // That's why we may not find proc as it was written in the create statement. // So we try out the upper and lower case names too. return new ProcedureInfo[] { new ProcedureInfo(null, null, simpleName, null, DatabaseMetaData.procedureResultUnknown, _dmd), new ProcedureInfo(null, null, simpleName.toUpperCase(), null, DatabaseMetaData.procedureResultUnknown, _dmd), new ProcedureInfo(null, null, simpleName.toLowerCase(), null, DatabaseMetaData.procedureResultUnknown, _dmd) }; } } private String removeQuotes(String simpleName) { if(simpleName.startsWith("\"")) { simpleName = simpleName.substring(1); } if(simpleName.endsWith("\"")) { simpleName = simpleName.substring(0, simpleName.length() - 1); } return simpleName; } private TableInfo[] getTableInfos(String sql) { sql = sql.trim(); String upperSql = sql.toUpperCase(); Matcher matcher; matcher = PATTERN_CREATE_TABLE.matcher(upperSql); if(matcher.find()) { return createTableInfos(matcher, sql, "TABLE", false); } matcher = PATTERN_ALTER_TABLE.matcher(upperSql); if(matcher.find()) { return createTableInfos(matcher, sql, "TABLE", true); } matcher = PATTERN_INSERT_INTO.matcher(upperSql); if(matcher.find()) { return createTableInfos(matcher, sql, "TABLE", false); } matcher = PATTERN_CREATE_VIEW.matcher(upperSql); if(matcher.find()) { return createTableInfos(matcher, sql, "VIEW", false); } matcher = PATTERN_ALTER_VIEW.matcher(upperSql); if(matcher.find()) { return createTableInfos(matcher, sql, "VIEW", true); } return new TableInfo[0]; } private TableInfo[] createTableInfos(Matcher matcher, String sql, String type, boolean isAlterOrDrop) { int endIx = matcher.end(1); int len = matcher.group(1).length(); String table = sql.substring(endIx - len, endIx); String[] splits = table.split("\\."); String simpleName = splits[splits.length - 1]; simpleName = removeQuotes(simpleName); if(isAlterOrDrop) { String buf = _session.getSchemaInfo().getCaseSensitiveTableName(simpleName); if(null != buf) { simpleName = buf; } return new TableInfo[]{new TableInfo(null, null, simpleName, type, null, _dmd)}; } else { // DB2 stores all names in upper case. // PostgreSQL stores table names in lower case. // That's why we may not find table as it was written in the create statement. // So we try out the upper and lower case names too. return new TableInfo[] { new TableInfo(null, null, simpleName, type, null, _dmd), new TableInfo(null, null, simpleName.toUpperCase(), type, null, _dmd), new TableInfo(null, null, simpleName.toLowerCase(), type, null, _dmd) }; } } } |