From: SVN by r. <sv...@ca...> - 2008-07-23 07:24:21
|
Author: roy Date: 2008-07-23 09:24:09 +0200 (Wed, 23 Jul 2008) New Revision: 265 Added: src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java src/main/java/nl/improved/sqlclient/InputKey.java src/main/java/nl/improved/sqlclient/jcurses/ src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java Log: abstraction layer in sqlshell window structure fixed 'quit' on hardy Added: src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java =================================================================== --- src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java 2008-07-16 21:03:05 UTC (rev 264) +++ src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java 2008-07-23 07:24:09 UTC (rev 265) @@ -0,0 +1,1501 @@ +/* + * Copyright 2007 Roy van der Kuil (ro...@va...) and Stefan Rotman (st...@ro...) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.improved.sqlclient; + +import java.io.*; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.LinkedHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import nl.improved.sqlclient.commands.*; + +/** + * The SQLShell main class. + * This class provides a (textbased) window for entering sql commands. + */ +public abstract class AbstractSQLShellWindow { + + /** + * The current command thread executing a SQLShell command. + */ + private CommandThread commandThread; + /** + * True when the prompt should be visible. + * Normally it is not visible during the execution of a command + */ + protected boolean showPrompt = true; + + /** + * The (default) maximum matches to show in a selection dialog. + */ + protected static final int MAX_MATCH_SIZE = 15; + protected int MAX_LINE_LENGTH; // not static and final.. since it can change on window resize + + /** + * Page up count 0 means were at the bottom of our screen. + * Increasement of the pageup count moves the visible part of the screen up. + */ + protected int pageUpCount = 0; + /** + * All lines in a screen. + */ + protected List<CharSequence> screenBuffer = new LimitedArrayList<CharSequence>(1000); + /** + * The current command lines. + */ + protected SQLCommand commandLines; + /** + * All commands in history. + */ + private List<SQLCommand> commandHistory = new LimitedArrayList<SQLCommand>(50); + /** + * Index for browsing commands. + */ + protected int commandIndex = 0; + /** + * An empty line for easy line completion (fill with spaces). + */ + protected String emptyLine; + /** + * The cursor position in the command lines list. + * 0,0 means it is at the first line at the first character + * 10,5 means it is at the 11th character at the 6th line + */ + protected Point cursorPosition = new Point(0,0); + /** + * The prompt to show when entering sql commands. + */ + protected static final String PROMPT = "SQL"; + /** + * Some debug info holding the last trace of an exception. + */ + private String lastExceptionDetails; + + /** + * The output file when spool is on. + */ + private Writer spoolWriter; + /** + * A manager for available commands. + */ + protected CommandManager commands = new CommandManager(); + + /** + * A map of string key representation to a action that should be executed when a specific key is pressed. + */ + private Map<String, KeyAction> actionKeys = new LinkedHashMap<String, KeyAction>(); + private Map<InputKey.SpecialKey, KeyAction> specialActionKeys = new LinkedHashMap<InputKey.SpecialKey, KeyAction>(); + private boolean paint = true; + + /** + * Constructor. + */ + public AbstractSQLShellWindow() { + char[] emptyLineChar = new char[getScreenWidth()]; + Arrays.fill(emptyLineChar, ' '); + emptyLine = new String(emptyLineChar); + + // initialize the command shell + commandLines = new SQLCommand(); + commandHistory.add(commandLines); + newLine(); + + // Register all known commands + commands.register("CONNECT[\\s]*.*", new ConnectCommand()); + commands.register("DISCONNECT[\\s]*", new DisConnectCommand()); + commands.register("SHOW[\\s]+[A-Z]+(|[A-Z]|_|\\.)*(|[\\s]+HAVING[\\s][A-Z]*.*)", new ShowCommand()); + commands.register("DESC[\\s]+[A-Z]+(|[0-9]|[A-Z]|_|-|\\.)*", new DescCommand()); + commands.register("INFO[\\s]*", new InfoCommand()); + commands.register("HELP[\\s]*.*", new HelpCommand()); + commands.register("HISTORY[\\s]*.*", new HistoryCommand()); + commands.register("SPOOL[\\s]*.*", new SpoolCommand()); + commands.register("QUIT[\\s]*", new QuitCommand("quit")); + commands.register("EXIT[\\s]*", new QuitCommand("exit")); + //commands.register("\\\\Q[\\s]*", new QuitCommand("\\q")); + commands.register("@.*", new ExecuteBatchCommand()); + commands.register("(SELECT|UPDATE|ALTER|INSERT|DELETE).*;[\\s]*", new QueryCommand()); + + // keys + specialActionKeys.put(InputKey.SpecialKey.LEFT, new KeyAction() { + public void execute() { + if (cursorPosition.x > 0) { + cursorPosition.x--; + } else if (cursorPosition.y > 0) { + cursorPosition.y--; + cursorPosition.x = commandLines.getLines().get(cursorPosition.y).length(); + } + } + public CharSequence getHelp() { + return "Arrow Left:\tMove cursor to the left"; + } + }); + specialActionKeys.put(InputKey.SpecialKey.RIGHT, new KeyAction() { + public void execute() { + CharSequence tmp = commandLines.getLines().get(cursorPosition.y); + if (cursorPosition.x < tmp.length()) { + cursorPosition.x++; + } else if (cursorPosition.y < commandLines.getLines().size()-1) { + cursorPosition.x = 0; + cursorPosition.y++; + } + } + public CharSequence getHelp() { + return "Arrow Right:\tMove cursor to the right"; + } + }); + specialActionKeys.put(InputKey.SpecialKey.UP, new KeyAction() { + public void execute() { + if (commandIndex > 0) { + commandLines = commandHistory.get(--commandIndex); + cursorPosition.y = commandLines.getLines().size()-1; + CharSequence lineBuffer = commandLines.getLines().get(cursorPosition.y); + cursorPosition.x = lineBuffer.length(); + } + } + public CharSequence getHelp() { + return "Arrow Up:\tBrowse to previous command in the history"; + } + }); + specialActionKeys.put(InputKey.SpecialKey.DOWN, new KeyAction() { + public void execute() { + if (commandIndex < commandHistory.size()-1) { + commandLines = commandHistory.get(++commandIndex); + cursorPosition.y = commandLines.getLines().size()-1; + CharSequence lineBuffer = commandLines.getLines().get(cursorPosition.y); + cursorPosition.x = lineBuffer.length(); + } + } + public CharSequence getHelp() { + return "Arrow Down:\tBrowse to next command in the history"; + } + }); + specialActionKeys.put(InputKey.SpecialKey.END,new KeyAction() { + public void execute() { + int curLineEnd = commandLines.getLines().get(cursorPosition.y).length(); + if (cursorPosition.x == curLineEnd) { + cursorPosition.y = commandLines.getLines().size()-1; + CharSequence lineBuffer = commandLines.getLines().get(cursorPosition.y); + cursorPosition.x = lineBuffer.length(); + } else { + cursorPosition.x = curLineEnd; + } + } + public CharSequence getHelp() { + return "End:\tMove the cursor to the end of the line, of if already there to the end of the command"; + } + }); + specialActionKeys.put(InputKey.SpecialKey.HOME, new KeyAction() { + public void execute() { + if (cursorPosition.x == 0) { + cursorPosition.y = 0; + } + cursorPosition.x = 0; + } + public CharSequence getHelp() { + return "Home:\tMove the cursor to the start of the line, of if already there to the start of the command"; + } + }); + specialActionKeys.put(InputKey.SpecialKey.BACKSPACE, new KeyAction() { + public void execute() { + if (cursorPosition.x == 0) { + if (cursorPosition.y > 0) { + joinLine(); + } + } else { + StringBuilder tmp = getEditableCommand().getEditableLines().get(cursorPosition.y); + if (cursorPosition.x > 0) { + tmp.deleteCharAt(cursorPosition.x-1); + cursorPosition.x--; + } + } + } + public CharSequence getHelp() { + return "Backspace:\tRemove the character before the cursor position"; + } + }); + specialActionKeys.put(InputKey.SpecialKey.DELETE, new KeyAction() { + public void execute() { + StringBuilder lineBuffer = commandLines.getEditableLines().get(cursorPosition.y); + if (cursorPosition.x < lineBuffer.length()) { + StringBuilder tmp = getEditableCommand().getEditableLines().get(cursorPosition.y); + tmp.deleteCharAt(cursorPosition.x); + } + } + public CharSequence getHelp() { + return "Del:\tDelete the charactor at the current cursor position"; + } + }); + specialActionKeys.put(InputKey.SpecialKey.PAGE_UP, new KeyAction() { + @Override + public void execute() { + if ((screenBuffer.size() + commandLines.getLines().size() + - (getScreenHeight()/2) * pageUpCount) > 0) { + pageUpCount++; + + } + } + public CharSequence getHelp() { + return "PageUp:\tMove back in screen history"; + } + }); + specialActionKeys.put(InputKey.SpecialKey.PAGE_DOWN, new KeyAction() { + @Override + public void execute() { + if (pageUpCount > 0) { + pageUpCount--; + } + } + public CharSequence getHelp() { + return "PageDown:\tMove forward in screen history"; + } + }); + actionKeys.put("", new KeyAction() { + public void execute() { // ctrl+w + if (cursorPosition.x == 0) { + if (cursorPosition.y > 0) { + joinLine(); + } + return; + } + StringBuilder lineBuffer = commandLines.getEditableLines().get(cursorPosition.y); + int previousBreak = SQLUtil.getLastBreakIndex(lineBuffer.substring(0, cursorPosition.x-1)); + if (lineBuffer.charAt(previousBreak) == ' ' || lineBuffer.charAt(previousBreak) == '\t') { + previousBreak++; + } + lineBuffer.delete(previousBreak, cursorPosition.x); + cursorPosition.x = previousBreak; + } + public CharSequence getHelp() { + return "Control-W:\tRemove word before cursor position"; + } + }); + actionKeys.put("", new KeyAction() { // ctrl+u + public void execute() { + if (cursorPosition.x > 0) { + StringBuilder lineBuffer = commandLines.getEditableLines().get(cursorPosition.y); + lineBuffer.delete(0, cursorPosition.x); + cursorPosition.x = 0; + } else if (cursorPosition.y > 0) { + StringBuilder lineBuffer = commandLines.getEditableLines().get(cursorPosition.y); + if (lineBuffer.length() == 0) { + commandLines.getEditableLines().remove(cursorPosition.y); + } + cursorPosition.y--; + lineBuffer = commandLines.getEditableLines().get(cursorPosition.y); + lineBuffer.delete(0, lineBuffer.length()); + } + } + public CharSequence getHelp() { + return "Control-U:\tRemove all characters before the cursor position"; + } + }); + actionKeys.put("", new KeyAction() { // ctrl+a + @Override + public void execute() { + output("Abort requested"); + if (commandThread.isAlive() && commandThread.getCommand().abort()) { + output("Abort done.."); + } + } + @Override + public CharSequence getHelp() { + return "Control-A:\tAbort current command (if it is supported by that command)"; + } + }); + actionKeys.put("", new KeyAction() { //Ctrl+D + @Override + public void execute() { + if (commandLines.getCommandString().length() == 0) { //Quit on empty commandline, ignore otherwise + executeCommand(new InputCommand("quit")); + } + } + @Override + public CharSequence getHelp() { + return "Control-D:\tExit sqlshell"; + } + }); + + MAX_LINE_LENGTH = getScreenWidth()-(PROMPT.length()+2+1); // prompt + "> " + + output("Welcome to the SQLShell client."); + output("Type 'help' to get a list of available commands other then the default sql input."); + } + + + public abstract int getScreenWidth(); + public abstract int getScreenHeight(); + private void repaint() { + if (paint) { + paint(); + } + } + public abstract void paint(); + public abstract void beep(); + public abstract void debug(String debug); + public abstract String select(List<String> items); + /** + * Returns the connection to the database + * @return the connection to the database + */ + public Connection getConnection() { + return DBConnector.getInstance().getConnection(); + } + + public void close() { + debug("CLOSE HERE"); + System.out.println("CLOSE HERE"); + if (spoolWriter != null) { + try { + spoolWriter.close(); + spoolWriter = null; + } catch(Exception e) {/*ignore*/} + } + } + + + /** + * Returns a string representation of the current command. + * @return a string representation of the current command. + */ + protected SQLCommand getCommand() { + return commandLines; + } + + /** + * Add a new line to the command lines buffer. + */ + private StringBuilder newLine() { + cursorPosition.x = 0; + StringBuilder newLine = new StringBuilder(); + getEditableCommand().getEditableLines().add(newLine); + cursorPosition.y = commandLines.getLines().size()-1; + return newLine; + } + + /** + * Handle key input. + * @param inp the character that is being pressed by the user. + */ + protected void handleInput(InputKey inp) { + try { + if (inp.isSpecialKey() && inp.getSpecialKeyValue() != InputKey.SpecialKey.PAGE_UP && inp.getSpecialKeyValue() != InputKey.SpecialKey.PAGE_DOWN) { + pageUpCount = 0; // some character entered, so reset pageup count + } + if (inp.isSpecialKey()) { + KeyAction ke = specialActionKeys.get(inp.getSpecialKeyValue());// || inp.toString().equals("") || inp.toString().equals("")) { + if (ke != null) { + ke.execute(); + } else { + debug("Unknown character: "+ inp.getCharacter()); + } + } else { + KeyAction ke = actionKeys.get(Character.toString(inp.getCharacter())); + if (ke != null) { + ke.execute(); + } else { + if (inp.getCharacter() == '\n') { // newline... see if the command can be executed + // execute the command + SQLCommand sqlCommand = getCommand(); + String command = sqlCommand.getCommandString(); + // search command... + if (command.length() > 0 && command.charAt(0) == '/') { // search in history + String matchPattern=".*"+command.substring(1,command.length())+".*"; + for (int cIndex = commandHistory.size()-1; cIndex >=0; cIndex--) { + if (cIndex == commandIndex) { + continue; // skip current command + } + SQLCommand newSqlCommand = commandHistory.get(cIndex); + String commandString = newSqlCommand.getCommandString(); + if (commandString.matches(matchPattern)) { + commandHistory.remove(commandIndex); + commandIndex = commandHistory.indexOf(newSqlCommand); + commandLines = newSqlCommand; + cursorPosition.y = 0; + cursorPosition.x = 0; + repaint(); // force repaint + return; + } + } + beep(); // TODO clear search?? + return; + /*} else if (sqlCommand.getCommandString().equalsIgnoreCase("exit")) { + close(); + return;*/ + } else if (executeCommand(sqlCommand)) { + // clear command history + if (commandIndex != commandHistory.size()-1) { + SQLCommand tmpLines = commandLines; + commandLines = commandHistory.get(commandHistory.size()-1); + if (commandLines.getCommandString().equals("")) { + commandHistory.add(commandHistory.size()-1, tmpLines); + } else { + commandHistory.add(tmpLines); + commandLines = tmpLines; + } + } + if (!commandLines.getCommandString().equals("")) { + commandLines = new SQLCommand(); + commandHistory.add(commandLines); + newLine(); + } + commandIndex = commandHistory.size()-1; + cursorPosition.y = commandLines.getLines().size()-1; + cursorPosition.x = commandLines.getLines().get(cursorPosition.y).length(); + repaint(); // force repaint + return; + } + } + CharSequence newText; + if (inp.getCharacter() == '\t') { + try { + newText = getTabCompletion(commandLines, cursorPosition); + } catch(IllegalStateException e) { + output(getCommand().getCommandString()); // add command as well... + error(e); + return; + } + } else { + newText = Character.toString(inp.getCharacter()); + } + if (newText.equals("\n")) { + newLine(); // TODO Fix return in middle of an other line + } else { + List<StringBuilder> editableLines = getEditableCommand().getEditableLines(); + StringBuilder currentLine = editableLines.get(cursorPosition.y); + currentLine.insert(cursorPosition.x, newText); + cursorPosition.x += newText.length(); + // check if the new line is becoming too long + if (currentLine.length() > MAX_LINE_LENGTH) { + // TODO search for lastspace that is not between '' ?? + int lastSpace = SQLUtil.getLastBreakIndex(currentLine.toString());//currentLine.lastIndexOf(" "); + if (lastSpace == -1) { + lastSpace = currentLine.length(); + } + // check if there are enough 'next' lines + // if not.. add one + if (editableLines.size()-1 == cursorPosition.y) { + StringBuilder newLine = new StringBuilder(); + editableLines.add(newLine); + } + // check if the nextline has enough room for the new word + // if not.. add a new line + if (editableLines.get(cursorPosition.y+1).length() + + (currentLine.length()-lastSpace+1) > MAX_LINE_LENGTH) { + StringBuilder newLine = new StringBuilder(); + editableLines.add(cursorPosition.y+1, newLine); + } + // fetch the next line + StringBuilder nextLine = editableLines.get(cursorPosition.y+1); + // if the nextline already has some text.. add a space in front of it + if (nextLine.length() > 0) { + nextLine.insert(0, ' '); + } + // insert the new text at the beginning + nextLine.insert(0, currentLine.subSequence(lastSpace+1, currentLine.length())); + currentLine.delete(lastSpace, currentLine.length()); + // check if the cursor postition > the new line length + // calculate new x and go to nextline + if (cursorPosition.x >= lastSpace) { + cursorPosition.x = cursorPosition.x - (lastSpace+1); + cursorPosition.y++; + } + } + } + } + } + } catch(Throwable t) { + error(t); + } + repaint(); + } + + /** + * Return the editable version of the commandlines. + * If editing a previous command clone it and return the clone + * @return the editable version of the commandlines. + */ + protected SQLCommand getEditableCommand() { + if (commandHistory.indexOf(commandLines) != commandHistory.size()-1) { + List<? extends CharSequence> tmp = commandLines.getLines(); + if (commandHistory.get(commandHistory.size()-1).getLines().size() == 1 + && commandHistory.get(commandHistory.size()-1).getLines().get(0).length() == 0) { + commandLines = commandHistory.get(commandHistory.size()-1); + commandLines.getEditableLines().remove(0); + } else { + commandLines = new SQLCommand(); + commandHistory.add(commandLines); + } + for (int i = 0; i < tmp.size(); i++) { + commandLines.getEditableLines().add(new StringBuilder(tmp.get(i))); + } + commandIndex = commandHistory.size()-1; + } + return commandLines; + } + + /** + * Output data to the screen. + * @param data the data to print to the screen. + */ + protected synchronized void output(CharSequence data) { + screenBuffer.addAll(getLines(data)); + if (spoolWriter != null) { + try { + spoolWriter.write(data.toString()); + spoolWriter.write("\n"); + } catch(IOException e) { + screenBuffer.add("WARNING: Could not write to spool file"); + error(e); + } + } + } + + /** + * Output error exception to the screen. + * @param e the error to print to the screen + */ + protected synchronized void error(Throwable e) { + output(e.getMessage() == null ? e.toString() : e.getMessage()); + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + sw.flush(); + lastExceptionDetails = sw.toString(); + //output(sw.toString()); + } + + /** + * Return a list of table names available for the current connection. + * @return a list of table names available for the current connection. + */ + protected List<String> getTableNames() { + List<String> returnValue = new ArrayList<String>(); + try { + ResultSet rs = getConnection().getMetaData().getTables(getConnection().getCatalog(), DBConnector.getInstance().getSchema(), null, new String[]{"TABLE"}); + while (rs.next()) { + if (!returnValue.contains(rs.getString("TABLE_NAME"))) { + returnValue.add(rs.getString("TABLE_NAME")); + } + } + return returnValue; + } catch (SQLException ex) { + ex.printStackTrace(); + Logger.getLogger(AbstractSQLShellWindow.class.getName()).log(Level.SEVERE, null, ex); + return null; + } + } + + /** + * Return a list of column names for the specified list of table names. + * @param tableNames a list of tableNames + * @return a list of column names for the specified list of table names. + */ + protected List<String> getColumnNames(List<String> tableNames) { + List<String> returnValues = new ArrayList<String>(); + Iterator<String> iTableNames = tableNames.iterator(); + while (iTableNames.hasNext()) { + String tableName = DBConnector.getInstance().translateDbVar(iTableNames.next().trim()); + try { + ResultSet rs = getConnection().getMetaData().getColumns(getConnection().getCatalog(), DBConnector.getInstance().getSchema(), tableName, "%"); + while (rs.next()) { + if (!returnValues.contains(rs.getString("COLUMN_NAME"))) { + returnValues.add(rs.getString("COLUMN_NAME")); + } + } + } catch (SQLException ex) { + throw new IllegalStateException("Failed to find columnnames for table: "+ tableName, ex); + } + } + return returnValues; + } + + /** + * Try to find a match in the provided list starging with sub. + * If more matches apply show the user a dialog to choose a match or simply display all matches in the window + * @param values all possible values to choose from (not limited with the sub parameter) + * @param sub the start of the match + * @return the match starting with sub minus the sub itself in the correct casing + */ + protected CharSequence findMatch(List<String> values, String sub) { + List<String> matches = new ArrayList<String>(); + Iterator<String> iValues = values.iterator(); + while (iValues.hasNext()) { + String value = iValues.next(); + if (value.toUpperCase().startsWith(sub.toUpperCase())) { + matches.add(value); + } + } + debug("Matches found: "+ matches.size() +" --> "+ sub +" / "+ values); + String match = null; + if (matches.size() == 1) { + match = matches.get(0); + } else if (matches.size() > 1 && matches.size() < MAX_MATCH_SIZE) { + match = select(matches); + } else if (matches.size() > 1) { + output("\n"+toColumns(matches)); + } + if (match != null) { + if (sub.length() > 0) { + /*if (Character.isUpperCase(sub.charAt(0))) { + match = match.toUpperCase(); + } else { + match = match.toLowerCase(); + }*/ + match = DBConnector.getInstance().translateDbVar(match); + } + return match.substring(sub.length()); + } + return null; + } + + /** + * Simple method to change a null value to an empty charsequence. + * @param s the charsequence to convert to empty if it is null + * @return empty when s was null or return s when s is not null + */ + protected CharSequence nullToEmpty(CharSequence s) { + if (s == null) { + return ""; + } + return s; + } + /** + * return the tab completion value. + * @param commandLines the current command + * @param cursorPosition the position where the tab completion was invocated + * @return the tab completion value. + */ + protected CharSequence getTabCompletion(SQLCommand commandLines, Point cursorPosition) { + TabCompletionInfo info = null; + String cmd = commandLines.getCommandString(); + if (cmd.length() > 0) { + if (cmd.indexOf(' ') > 0) { + cmd = cmd.substring(0, cmd.indexOf(' ')).trim(); + } + Command tmpCommand = commands.findCommand(cmd); + if (tmpCommand == null) { + for (Command c : commands.getCommands()) { + if (cmd.equalsIgnoreCase(c.getCommandString().toString())) { + tmpCommand = c; + break; + } + } + } + if (tmpCommand != null) { + info = tmpCommand.getTabCompletionInfo(commandLines, cursorPosition); + } + } + if (info == null) { + info = SQLUtil.getTabCompletionInfo(commandLines, cursorPosition); + } + if (info.getMatchType() == TabCompletionInfo.MatchType.SQL_KEYWORD) { + return nullToEmpty(findMatch(info.getPossibleMatches(), info.getStart())); + } + if (info.getMatchType() == TabCompletionInfo.MatchType.TABLE_NAMES) { + debug("table completion for \""+info.getStart()+"\""); + return nullToEmpty(findMatch(getTableNames(), info.getStart())); + } + if (info.getMatchType() == TabCompletionInfo.MatchType.COLUMN_NAMES) { + return nullToEmpty(findMatch(getColumnNames(info.getPossibleMatches()), info.getStart())); + } + if (info.getMatchType() == TabCompletionInfo.MatchType.OTHER) { + return nullToEmpty(findMatch(info.getPossibleMatches(), info.getStart())); + } + return ""; + } + + /** + * (Try) to execute a command) and return true if it succeeded. + * @param command the command to try and execute + * @return true if it succeeded. + */ + protected boolean executeCommand(SQLCommand sqlCommand) { + return executeCommand(sqlCommand, false); + } + + private boolean executeCommand(final SQLCommand sqlCommand, boolean direct) { + final String commandString = sqlCommand.getCommandString(); + if (commandString.equalsIgnoreCase("printStackTrace")) { + if (lastExceptionDetails == null) { + output("No known last exception to print"); + } else { + output(lastExceptionDetails); + } + return true; + } + Command command = createCommand(commandString); // first try to find a match without ; + if (command == null) { + command = createCommand(sqlCommand.getUntrimmedCommandString()); // then with ; for sql statements... + } + if (command == null) { + return false; + } + // make sure only one command is run at once + if (commandThread != null && commandThread.isAlive()) { + try { + commandThread.join(); + } catch (InterruptedException ex) { + Logger.getLogger(AbstractSQLShellWindow.class.getName()).log(Level.SEVERE, null, ex); + } + } + output(commandString); + if (direct || !command.backgroundProcessSupported()) { + output(command.execute(sqlCommand)); + } else { + commandThread = new CommandThread(command) { + @Override + void execute() { + output(getCommand().execute(sqlCommand)); + } + }; + commandThread.start(); + } + return true; + } + private Command createCommand(String commandString) { + Command command = commands.findCommand(commandString); + if (command != null) { + return command; + } + if (commandString.matches(".*;[\\s]*")) { + return new QueryCommand(); // TODO is this ever reached??? + } + return null; + } + + + + /** + * Method to convert a long string to a displayable list of strings with a max with of the screen width. + * @param text a (long) string + * @return the text devided into multiple lines to match the screen width + */ + private List<CharSequence> getLines(CharSequence text) { + int maxWidth = getScreenWidth(); + List<CharSequence> list = new ArrayList<CharSequence>(); + StringBuilder buffer = new StringBuilder(); + for (int i=0; i<text.length(); i++) { + char c = text.charAt(i); + if (c=='\n' || buffer.length() == maxWidth) { + String line = buffer.toString(); + list.add(line); + buffer = new StringBuilder(); + if (c != '\n') { + buffer.append(c); + } + } else if (c == '\r') { + //ignore + } else { + buffer.append(c); + } + } + if (buffer.length() > 0) { + list.add(buffer.toString()); + } + return list; + } + + /** + * Convert a list to a presentable text devided into multiple columns. + */ + private StringBuilder toColumns(List<? extends CharSequence> values) { + int maxWidth = 0; + Iterator<? extends CharSequence> iValues = values.iterator(); + while (iValues.hasNext()) { + maxWidth = Math.max(maxWidth, iValues.next().length()); + } + maxWidth+=2;// add extra space + int nrOfColumns = (getScreenWidth()) / maxWidth; + StringBuilder returnValue = new StringBuilder(); + for (int row = 0; row < values.size(); row+=nrOfColumns) { + for (int col = 0; col < nrOfColumns && row + col < values.size(); col++) { + returnValue.append(values.get(row+col) + emptyLine.substring(0, maxWidth - values.get(row+col).length())); + } + returnValue.append('\n'); + } + return returnValue; + } + + /** + * A list which contains a limited number of lines. + */ + private class LimitedArrayList<E> extends ArrayList<E> { + private int maxSize; + /** + * Constructor. + * @param maxSize the maximum number of lines the list may contain + */ + public LimitedArrayList(int maxSize) { + super(maxSize); // some sane default + this.maxSize = maxSize; + } + + @Override + public boolean add(E object) { + if (size() == maxSize) { + remove(0); + } + return super.add(object); + } + @Override + public void add(int index, E object) { + if (size() == maxSize) { + remove(0); + } + super.add(index, object); + } + } + + /** + * Connect command for setting up a connection to a database. + */ + private static class ConnectCommand implements Command { + /** + * Execute the connection command and return a readable result. + * @param command the command string for setting up a connection + * @return a readable result of the execution of this command. + */ + @Override + public CharSequence execute(SQLCommand cmd) { + String command = cmd.getCommandString(); + try { + String cmdString = command.substring("connect".length()).trim(); + if (cmdString.length() > 0 && cmdString.charAt(cmdString.length()-1) == ';') { + cmdString = cmdString.substring(0, cmdString.length()-1); + } + DBConnector.getInstance().connect(cmdString); + return "Connected.\n\n"; + } catch(SQLException e) { + throw new IllegalStateException("Failed to connect: " + e.getMessage(), e); + } + } + @Override + public CharSequence getCommandString() { + return "connect"; + } + + /** + * Returns some tab completion info for the specified command. + * @param commandInfo the command lines + * @param commandPoint the cursor position + * @return some tab completion info for the specified command. + */ + @Override + public TabCompletionInfo getTabCompletionInfo(SQLCommand command, Point commandPoint) { + List commandInfo = command.getLines(); + + String startOfCommand = SQLUtil.getStartOfCommand(commandInfo, commandPoint); + if (startOfCommand.indexOf('@') >= 0) { + String end = startOfCommand.substring(startOfCommand.lastIndexOf('@')+1); + List<String> identifiers = new ArrayList<String>(DBConnector.getInstance().getPredefinedConnectionIdentifiers()); + Collections.sort(identifiers); + return new TabCompletionInfo(TabCompletionInfo.MatchType.OTHER, identifiers, end); + } + return null; + } + + @Override + public CharSequence getHelp() { + StringBuffer buf = new StringBuffer(); + Iterator<String> idents = DBConnector.getInstance().getPredefinedConnectionIdentifiers().iterator(); + while (idents.hasNext()) { + buf.append(' '); + String ident = idents.next(); + buf.append(ident); + if (ident.equals(DBConnector.getInstance().getDefaultIdentifier())) { + buf.append(" *"); + } + if (idents.hasNext()) { + buf.append('\n'); + } + } + return "Create a conection to the database\n" + + " user/pass@ident -> connect the user with password pass to the ident\n" + + " user/@ident -> connect the user with an empty password pass to the ident\n"+ + " user@ident -> connect the user to the ident and prompt for password\n"+ + " @ident -> connect to the ident connection.\n"+ + " If default user and/or password are specified these will be used, \n"+ + " otherwise you will be prompted for a username and/or password.\n" + + "Currently configured connection identifiers:\n"+ + buf.toString(); + } + + @Override + public boolean abort() { + return false;// not implemented + } + @Override + public boolean backgroundProcessSupported() { + return false; + } + } + /** + * Command that enables the user to close a connection. + */ + private static class DisConnectCommand implements Command { + @Override + public CharSequence execute(SQLCommand cmd) { + try { + DBConnector.getInstance().disconnect(); + return "Disconnected.\n\n"; + } catch(SQLException e) { + throw new IllegalStateException("Failed to disconnect: " + e.getMessage(), e); + } + } + @Override + public CharSequence getCommandString() { + return "disconnect"; + } + + /** + * Returns some tab completion info for the specified command. + * @param commandInfo the command lines + * @param commandPoint the cursor position + * @return some tab completion info for the specified command. + */ + @Override + public TabCompletionInfo getTabCompletionInfo(SQLCommand command, Point commandPoint) { + return null; + } + @Override + public CharSequence getHelp() { + return "Close the current conection to the database"; + } + @Override + public boolean abort() { + return false;// not implemented + } + @Override + public boolean backgroundProcessSupported() { + return false; + } + } + + /** + * Provide a list of commands executed. + */ + private class HistoryCommand implements Command { + @Override + public CharSequence execute(SQLCommand command) { + StringBuilder returnValue = new StringBuilder(); + Iterator<SQLCommand> iCommands = commandHistory.iterator(); + while (iCommands.hasNext()) { + SQLCommand cmd = iCommands.next(); + returnValue.append(cmd.getCommandString()); + returnValue.append('\n'); + } + return returnValue; + } + @Override + public CharSequence getCommandString() { + return "history"; + } + + /** + * Returns some tab completion info for the specified command. + * @param commandInfo the command lines + * @param commandPoint the cursor position + * @return some tab completion info for the specified command. + */ + @Override + public TabCompletionInfo getTabCompletionInfo(SQLCommand command, Point commandPoint) { + return null; + } + @Override + public CharSequence getHelp() { + return "Show history of executed statements\n" + + "By using '/<search>' you can search the command history" ; + } + @Override + public boolean abort() { + return false;// not implemented + } + @Override + public boolean backgroundProcessSupported() { + return false; + } + } + + /** + * Exit the client. + */ + private class QuitCommand implements Command { + private String cmd; + + public QuitCommand(String cmd) { + this.cmd = cmd; + } + @Override + public CharSequence execute(SQLCommand command) { + paint = false; + close(); + return "Application terminated."; + } + @Override + public CharSequence getHelp() { + return "Quit(exit) the application."; + } + @Override + public CharSequence getCommandString() { + return cmd; + } + /** + * Returns some tab completion info for the specified command. + * @param commandInfo the command lines + * @param commandPoint the cursor position + * @return some tab completion info for the specified command. + */ + @Override + public TabCompletionInfo getTabCompletionInfo(SQLCommand command, Point commandPoint) { + return null; + } + @Override + public boolean abort() { + return false;// not implemented + } + @Override + public boolean backgroundProcessSupported() { + return false; + } + } + /** + * Provide help to the user. + */ + private class HelpCommand implements Command { + @Override + public CharSequence execute(SQLCommand sqlCommand) { + // the execution of help consists of: + // 1. is general help.. + // 2. is detailed help about a specific command + // 3. help -k something --> search help for a specific keyword + String command = sqlCommand.getCommandString().trim(); + String cmdString = command.substring("help".length()).trim(); + if (cmdString.endsWith(";")) { + cmdString = cmdString.substring(0, cmdString.length()-1); + } + List<CharSequence> availableCommands = new ArrayList<CharSequence>(); + if (cmdString.length() > 0) { + if (cmdString.startsWith("-k")) { + String keyword = cmdString.subSequence(cmdString.indexOf(" "), cmdString.length()).toString().trim(); + StringBuilder returnValue = new StringBuilder(); + Iterator<Command> iCommands = commands.getCommands().iterator(); + while (iCommands.hasNext()) { + Command cmd= iCommands.next(); + if (cmd.getHelp().toString().indexOf(keyword) >=0 + || cmd.getCommandString().toString().indexOf(keyword) >=0) { + if (returnValue.length() == 0) { + returnValue.append("See the following commands for more details:\n"); + } + returnValue.append(" "); + returnValue.append(cmd.getCommandString()); + returnValue.append("\n"); + } + } + if (returnValue.length() == 0) { + return "Don't know what you mean by '"+ keyword+"'"; + } + return returnValue; + } else { + Iterator<Command> iCommands = commands.getCommands().iterator(); + while (iCommands.hasNext()) { + Command cmd= iCommands.next(); + if (cmd.getCommandString().equals(cmdString)) { + return cmd.getCommandString()+": " + + cmd.getHelp().toString().replaceAll("\n" + , "\n"+emptyLine.substring(0, cmd.getCommandString().length()+3)); + } + } + } + return "Unkown command '"+ cmdString+"'"; + } + // default print all commands + // TODO iterate + StringBuilder returnValue = new StringBuilder(); + returnValue.append("Available key mappings:\n"); + int max = 0; + Iterator<KeyAction> iKeyActions = actionKeys.values().iterator(); + while (iKeyActions.hasNext()) { + max = Math.max(max, iKeyActions.next().getHelp().toString().indexOf('\t')); + } + iKeyActions = actionKeys.values().iterator(); + while (iKeyActions.hasNext()) { + returnValue.append(" "); + //returnValue.append(iKeyActions.next().getHelp()); + String help = iKeyActions.next().getHelp().toString(); + int index = help.indexOf('\t'); + returnValue.append(help.substring(0, index)); + for (int i = index; i < max; i++) { + returnValue.append(' '); + } + returnValue.append(help.substring(index)); + returnValue.append('\n'); + } + returnValue.append("\n\nAvailable commands:\n"); + Iterator<Command> iCommands = commands.getCommands().iterator(); + while (iCommands.hasNext()) { + Command cmd = iCommands.next(); + availableCommands.add(cmd.getCommandString()); + } + returnValue.append(toColumns(availableCommands)); + String helpHeader = "\nHelp for SQLShell client "+SQLProperties.getProperty(SQLProperties.PropertyName.VERSION, "SVN Snapshot")+"\n"+ + "Here you find a list of available commands. "+ + "To get more information about a specific command enter:\n"+ + " help command (for example 'help help')\n\n"+ + "If the list is not sufficient enough you could try searching help using:\n"+ + " help -k searchstring (for example help -k column)\n"+ + "This results in a list of commands matching the searchstring\n\n"; + returnValue.insert(0, helpHeader); + return returnValue; + } + @Override + public CharSequence getCommandString() { + return "help"; + } + + /** + * Returns some tab completion info for the specified command. + * @param commandInfo the command lines + * @param commandPoint the cursor position + * @return some tab completion info for the specified command. + */ + @Override + public TabCompletionInfo getTabCompletionInfo(SQLCommand command, Point commandPoint) { + return null; + } + @Override + public CharSequence getHelp() { + return "this command. Please use 'help' to get a list of available commands you can use."; + } + @Override + public boolean abort() { + return false;// not implemented + } + @Override + public boolean backgroundProcessSupported() { + return false; + } + } + + /** + * Convert '~/' to the username dir. + * @param fileName the filename to convert + * @return the converted filename + */ + private static String toFileName(String fileName) { + if (fileName.startsWith("~/")) { + return System.getProperty("user.home")+fileName.substring(1); + } + return fileName; + } + + /** + * Writes in/output to a file. + */ + private class SpoolCommand implements Command { + private String fileName; + + @Override + public CharSequence execute(SQLCommand cmd) { + String command = cmd.getCommandString(); + String nextPart = command.substring("spool".length()).trim(); + if (nextPart.equalsIgnoreCase("off")) { + if (spoolWriter != null) { + try { + spoolWriter.close(); + } catch(Exception e) {/*ignore*/} + spoolWriter = null; + return "Spool closed."; + } else { + return "No spool to close."; + } + } else { + try { + File f = new File(toFileName(nextPart.trim())); + fileName = f.getAbsolutePath(); + if ((f.exists() && !f.canWrite()) || (!f.exists() && !f.createNewFile())) { + throw new IllegalStateException("Failed to create spool to file: '"+fileName+"'"); + } + spoolWriter = new FileWriter(fileName); + } catch (IOException e) { + throw new IllegalStateException("Failed to create spool ("+fileName+"): " + e.toString(), e); + } + return "Spool to "+fileName+" created."; + } + } + + @Override + public CharSequence getCommandString() { + return "spool"; + } + + /** + * Returns some tab completion info for the specified command. + * @param commandInfo the command lines + * @param commandPoint the cursor position + * @return some tab completion info for the specified command. + */ + @Override + public TabCompletionInfo getTabCompletionInfo(SQLCommand command, Point commandPoint) { + return null; + } + @Override + public CharSequence getHelp() { + return "filename: Spool all output and queries to the specified file\n"+ + "off : Stop spooling data to the file.\n" + + "Current status:"+(spoolWriter != null ? "on, writing to '"+fileName+"'" : "off"); + } + @Override + public boolean abort() { + return false;// not implemented + } + @Override + public boolean backgroundProcessSupported() { + return false; + } + } + + private class ExecuteBatchCommand implements Command { + private boolean cancelled; + private Command currentCommand; + @Override + public CharSequence execute(SQLCommand sqlCommand) { + cancelled = false; + currentCommand = null; + String command = sqlCommand.getCommandString(); + // read file from file system and execute + FileInputStream fin = null; + try { + fin = new FileInputStream(toFileName(command.substring(1))); + output("Reading file: "+ toFileName(command.substring(1))); + BufferedReader reader = new BufferedReader(new InputStreamReader(fin)); + StringBuilder cmd = new StringBuilder(); + String line; + while ( ((line = reader.readLine()) != null)) { + if (cancelled) { + return "Aborted"; + } + if (line.startsWith("--")) { + continue; + } + cmd.append(line); + if (line.endsWith(";")) { + // Exec cmd + String commandString = cmd.toString(); + currentCommand = createCommand(commandString); + output(commandString); + output(currentCommand.execute(new InputCommand(commandString))); + cmd=new StringBuilder(); + new Thread() { public void run() { repaint();}}.start(); + } + } + } catch(IOException e) { + error(e); + } finally { + if (fin != null) try { fin.close();}catch(Exception e) {/*ignore*/} + if (cancelled) { + return "Execution of file '" + toFileName(command.substring(1)) +"' aborted...."; + } + return "File '" + toFileName(command.substring(1)) +"' executed successfully."; + } + } + + @Override + public CharSequence getCommandString() { + return "@"; + } + + /** + * Returns some tab completion info for the specified command. + * @param commandInfo the command lines + * @param commandPoint the cursor position + * @return some tab completion info for the specified command. + */ + @Override + public TabCompletionInfo getTabCompletionInfo(SQLCommand command, Point commandPoint) { + String fileName = command.getCommandString().substring(1).trim(); // cutoff '@' + String dirName; + if (fileName.equals("")) { + fileName = "."; + dirName = "."; + } else { + fileName = toFileName(fileName); + if (fileName.indexOf('/') >= 0) { + File file = new File(fileName); + if (file.isDirectory()) { + fileName = ""; + dirName = file.getAbsolutePath()+"/"; + } else { + fileName = file.getName(); + dirName = file.getParent(); + } + } else { + dirName = "."; + } + } + return new TabCompletionInfo(TabCompletionInfo.MatchType.OTHER,Arrays.asList(new File(dirName).list()) , fileName); + } + + @Override + public CharSequence getHelp() { + return "Specify filename to execute a 'batch' command.\n"+ + "For example '@mystatements.sql' executes all statements part of the mystatements.sql file in the current directory."+ + "Note that all statements must be terminated with ';' (sql statements as well as connect statements or spool)"; + } + @Override + public boolean abort() { + cancelled = true; + if (currentCommand != null) { + currentCommand.abort(); + } + return true; + } + + @Override + public boolean backgroundProcessSupported() { + return true; + } + } + + /** + * Command class to execute a 'custom command'. + * this makes it possible to have 'automated' commands executed. + * E.g.: + * executeCommand(new InputCommand("connect")); + * will eventually execute the Connect command. + */ + private static class InputCommand extends SQLCommand { + private StringBuilder command; + + public InputCommand(String command) { + this.command = new StringBuilder(command); + } + + public InputCommand(StringBuilder command) { + this.command = command; + } + + @Override + public String getUntrimmedCommandString() { + return command.toString(); + } + @Override + public List<StringBuilder> getEditableLines() { + return Arrays.asList(new StringBuilder[]{command}); + } + @Override + public List<? extends CharSequence> getLines() { + return Arrays.asList(new StringBuilder[]{command}); + } + } + + private abstract class CommandThread extends Thread { + private Command cmd; + public CommandThread(Command cmd) { + this.cmd = cmd; + } + + public final void run() { + showPrompt = false; + try { + execute(); + } finally { + showPrompt = true; + repaint(); + } + } + + abstract void execute(); + + Command getCommand() { + retu... [truncated message content] |
From: SVN by r. <sv...@ca...> - 2008-07-31 07:59:19
|
Author: roy Date: 2008-07-31 09:59:08 +0200 (Thu, 31 Jul 2008) New Revision: 275 Modified: src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java Log: some threading fixes fix 'case sensitive table names' when using tabcompletion Modified: src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java =================================================================== --- src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java 2008-07-28 20:59:50 UTC (rev 274) +++ src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java 2008-07-31 07:59:08 UTC (rev 275) @@ -403,6 +403,7 @@ @Override void execute() { output(getCommand().execute(cmd.sql)); + repaint(); } }; commandThread.start(); @@ -421,10 +422,12 @@ public abstract int getScreenWidth(); public abstract int getScreenHeight(); - protected synchronized void repaint() { + protected void repaint() { if (paint) { try { - paint(screen); + synchronized(this) { + paint(screen); + } } catch(Throwable t) { error(t); } @@ -645,25 +648,29 @@ * Output data to the screen. * @param data the data to print to the screen. */ - protected synchronized void output(List<? extends CharSequence> data) { - for(CharSequence c : data) { - output(c); + protected void output(List<? extends CharSequence> data) { + synchronized(this) { + for(CharSequence c : data) { + output(c); + } } } /** * Output data to the screen. * @param data the data to print to the screen. */ - protected synchronized void output(CharSequence data) { - List<CharSequence> screenBuffer = screen.getScreenBuffer(); - screenBuffer.addAll(getLines(data)); - if (spoolWriter != null) { - try { - spoolWriter.write(data.toString()); - spoolWriter.write("\n"); - } catch(IOException e) { - screenBuffer.add("WARNING: Could not write to spool file"); - error(e); + protected void output(CharSequence data) { + synchronized(this) { + List<CharSequence> screenBuffer = screen.getScreenBuffer(); + screenBuffer.addAll(getLines(data)); + if (spoolWriter != null) { + try { + spoolWriter.write(data.toString()); + spoolWriter.write("\n"); + } catch(IOException e) { + screenBuffer.add("WARNING: Could not write to spool file"); + error(e); + } } } } @@ -759,13 +766,15 @@ output("\n"+toColumns(matches)); } if (match != null) { - if (sub.length() > 0) { - /*if (Character.isUpperCase(sub.charAt(0))) { - match = match.toUpperCase(); - } else { - match = match.toLowerCase(); - }*/ - match = DBConnector.getInstance().translateDbVar(match); + match = DBConnector.getInstance().translateDbVar(match); + if (sub.length() > 0 && !match.startsWith(sub)) { // case insensitive change + Point cursorPosition = screen.getCursorPosition(); + List<StringBuilder> lines = getEditableCommand().getEditableLines(); + if (lines.get(cursorPosition.y).length() >=sub.length()) { + cursorPosition.x-=sub.length(); + lines.get(cursorPosition.y).delete(cursorPosition.x, cursorPosition.x + sub.length()); + return match; + } } return match.substring(sub.length()); } Modified: src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java =================================================================== --- src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java 2008-07-28 20:59:50 UTC (rev 274) +++ src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java 2008-07-31 07:59:08 UTC (rev 275) @@ -39,8 +39,7 @@ private Window window; public SQLShellWindow() { - //super(getScreenWidth(),getScreenHeight(), true, "SQLShell"); - commands.register("WINDOW[\\s]+[A-Z]+", new WindowCommand()); + //commands.register("WINDOW[\\s]+[A-Z]+", new WindowCommand()); } @@ -54,9 +53,12 @@ return Toolkit.getScreenHeight(); } + @Override public void debug(String debug) { - CharColor color = new CharColor(CharColor.BLUE, CharColor.YELLOW); - Toolkit.printString(debug, 1, getScreenHeight()-1, color); + synchronized(this) { + CharColor color = new CharColor(CharColor.BLUE, CharColor.YELLOW); + Toolkit.printString(debug, 1, getScreenHeight()-1, color); + } } @@ -72,60 +74,6 @@ */ @Override protected synchronized void paint() { - /* - CharColor color = new CharColor(CharColor.BLACK, CharColor.WHITE, CharColor.BOLD, CharColor.BOLD); - - List<CharSequence> tmpList = new ArrayList<CharSequence>(); - tmpList.addAll(screenBuffer); - - //add prompt - List<? extends CharSequence> currentLines = commandLines.getLines(); - for (int i = 0; i < currentLines.size(); i++) { - if (i == 0 && showPrompt) { - tmpList.add(PROMPT+"> "+currentLines.get(i)); - } else { - String nrI = Integer.toString(i+1); - tmpList.add(emptyLine.substring(0,PROMPT.length() - nrI.length()) + nrI+"> "+currentLines.get(i)); - } - } - int startLine; - if (tmpList.size() > getScreenHeight()-1) { - startLine = tmpList.size() - (getScreenHeight()-1); - if (pageUpCount > 0) { - startLine -= (pageUpCount * getScreenHeight()/2); - if (startLine < 0) { - startLine = 0; - } - } - } else { - startLine = 0; - } - int lineNr; - for (lineNr = startLine;lineNr < tmpList.size() && lineNr - startLine < getScreenHeight()-1; lineNr++) { - CharSequence linePart = tmpList.get(lineNr); - String line = linePart.length() >= emptyLine.length() ? linePart.toString() : linePart + emptyLine.substring(linePart.length()); - Toolkit.printString(line - , 0, lineNr-startLine, color); - } - for (int lNr = lineNr; lNr < getScreenHeight(); lNr++) { - Toolkit.printString(emptyLine, 0, lNr, color); - } - - // paint cursor - color = new CharColor(CharColor.BLACK, CharColor.WHITE, CharColor.REVERSE, CharColor.REVERSE); - String cursorChar = " "; - if (commandLines.getLines().size() > 0) { - String tmp = commandLines.getLines().get(cursorPosition.y).toString(); - if (cursorPosition.x < 0) { - //debug("Cursor position was: "+ cursorPosition +" fixing"); - cursorPosition.x = 0; - } - if (cursorPosition.x < tmp.length()) { - cursorChar = tmp.substring(cursorPosition.x, cursorPosition.x+1); - } - } - Toolkit.printString(cursorChar, PROMPT.length() +"> ".length() + cursorPosition.x, lineNr-(commandLines.getLines().size() -cursorPosition.y)-startLine, color); - * */ SQLShellWindow.this.repaint(); } @@ -234,60 +182,62 @@ @Override public void paint(Screen screen) { - CharColor color = new CharColor(CharColor.BLACK, CharColor.WHITE, CharColor.BOLD, CharColor.BOLD); + synchronized(this) { + CharColor color = new CharColor(CharColor.BLACK, CharColor.WHITE, CharColor.BOLD, CharColor.BOLD); - List<CharSequence> tmpList = new ArrayList<CharSequence>(); - List<CharSequence> screenBuffer = screen.getScreenBuffer(); - tmpList.addAll(screenBuffer); + List<CharSequence> tmpList = new ArrayList<CharSequence>(); + List<CharSequence> screenBuffer = screen.getScreenBuffer(); + tmpList.addAll(screenBuffer); - //add prompt - List<? extends CharSequence> currentLines = commandLines.getLines(); - for (int i = 0; i < currentLines.size(); i++) { - if (i == 0 && screen.getShowPrompt()) { - tmpList.add(Screen.PROMPT+"> "+currentLines.get(i)); - } else { - String nrI = Integer.toString(i+1); - tmpList.add(screen.getEmptyLine().substring(0,Screen.PROMPT.length() - nrI.length()) + nrI+"> "+currentLines.get(i)); + //add prompt + List<? extends CharSequence> currentLines = commandLines.getLines(); + for (int i = 0; i < currentLines.size(); i++) { + if (i == 0 && screen.getShowPrompt()) { + tmpList.add(Screen.PROMPT+"> "+currentLines.get(i)); + } else { + String nrI = Integer.toString(i+1); + tmpList.add(screen.getEmptyLine().substring(0,Screen.PROMPT.length() - nrI.length()) + nrI+"> "+currentLines.get(i)); + } } - } - int startLine; - if (tmpList.size() > Toolkit.getScreenHeight()-1) { - startLine = tmpList.size() - (Toolkit.getScreenHeight()-1); - if (screen.getPageUpCount() > 0) { - startLine -= (screen.getPageUpCount() * Toolkit.getScreenHeight()/2); - if (startLine < 0) { - startLine = 0; + int startLine; + if (tmpList.size() > Toolkit.getScreenHeight()-1) { + startLine = tmpList.size() - (Toolkit.getScreenHeight()-1); + if (screen.getPageUpCount() > 0) { + startLine -= (screen.getPageUpCount() * Toolkit.getScreenHeight()/2); + if (startLine < 0) { + startLine = 0; + } } + } else { + startLine = 0; } - } else { - startLine = 0; - } - int lineNr; - for (lineNr = startLine;lineNr < tmpList.size() && lineNr - startLine < Toolkit.getScreenHeight()-1; lineNr++) { - CharSequence linePart = tmpList.get(lineNr); - String line = linePart.length() >= screen.getEmptyLine().length() ? linePart.toString() : linePart + screen.getEmptyLine().substring(linePart.length()); - Toolkit.printString(line - , 0, lineNr-startLine, color); - } - for (int lNr = lineNr; lNr < Toolkit.getScreenHeight(); lNr++) { - Toolkit.printString(screen.getEmptyLine(), 0, lNr, color); - } - - // paint cursor - color = new CharColor(CharColor.BLACK, CharColor.WHITE, CharColor.REVERSE, CharColor.REVERSE); - String cursorChar = " "; - Point cursorPosition = screen.getCursorPosition(); - if (commandLines.getLines().size() > 0) { - String tmp = commandLines.getLines().get(cursorPosition.y).toString(); - if (cursorPosition.x < 0) { - debug("Cursor position was: "+ cursorPosition +" fixing"); - cursorPosition.x = 0; + int lineNr; + for (lineNr = startLine;lineNr < tmpList.size() && lineNr - startLine < Toolkit.getScreenHeight()-1; lineNr++) { + CharSequence linePart = tmpList.get(lineNr); + String line = linePart.length() >= screen.getEmptyLine().length() ? linePart.toString() : linePart + screen.getEmptyLine().substring(linePart.length()); + Toolkit.printString(line + , 0, lineNr-startLine, color); } - if (cursorPosition.x < tmp.length()) { - cursorChar = tmp.substring(cursorPosition.x, cursorPosition.x+1); + for (int lNr = lineNr; lNr < Toolkit.getScreenHeight(); lNr++) { + Toolkit.printString(screen.getEmptyLine(), 0, lNr, color); } + + // paint cursor + color = new CharColor(CharColor.BLACK, CharColor.WHITE, CharColor.REVERSE, CharColor.REVERSE); + String cursorChar = " "; + Point cursorPosition = screen.getCursorPosition(); + if (commandLines.getLines().size() > 0) { + String tmp = commandLines.getLines().get(cursorPosition.y).toString(); + if (cursorPosition.x < 0) { + debug("Cursor position was: "+ cursorPosition +" fixing"); + cursorPosition.x = 0; + } + if (cursorPosition.x < tmp.length()) { + cursorChar = tmp.substring(cursorPosition.x, cursorPosition.x+1); + } + } + Toolkit.printString(cursorChar, Screen.PROMPT.length() +"> ".length() + cursorPosition.x, lineNr-(commandLines.getLines().size() -cursorPosition.y)-startLine, color); } - Toolkit.printString(cursorChar, Screen.PROMPT.length() +"> ".length() + cursorPosition.x, lineNr-(commandLines.getLines().size() -cursorPosition.y)-startLine, color); } public static void main(String[] args) { |
From: SVN by r. <sv...@ca...> - 2008-08-02 10:11:31
|
Author: roy Date: 2008-08-02 12:11:20 +0200 (Sat, 02 Aug 2008) New Revision: 277 Modified: src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java src/main/java/nl/improved/sqlclient/Screen.java src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java Log: finally (really??) fix paint issues added some comments code cleanup Modified: src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java =================================================================== --- src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java 2008-07-31 07:59:52 UTC (rev 276) +++ src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java 2008-08-02 10:11:20 UTC (rev 277) @@ -54,8 +54,8 @@ import org.w3c.dom.NodeList; /** - * The SQLShell main class. - * This class provides a (textbased) window for entering sql commands. + * The SQLShell abstract main class. + * This class provides a the basic implementation for entering sql commands. A super implementation is required to show the actual data. */ public abstract class AbstractSQLShellWindow { @@ -89,16 +89,28 @@ */ protected CommandManager commands = new CommandManager(); - private ArrayBlockingQueue<CommandInfo> commandQueue = new ArrayBlockingQueue<CommandInfo>(10, true); // true means fair + /** + * The command list of commands to execute. + */ + private ArrayBlockingQueue<CommandInfo> commandQueue = new ArrayBlockingQueue<CommandInfo>(99, true); // true means fair + /** + * The screen information. + */ private Screen screen; /** * A map of string key representation to a action that should be executed when a specific key is pressed. */ private Map<String, KeyAction> actionKeys = new LinkedHashMap<String, KeyAction>(); + /** + * The special (home, end, up, down, etc) key actions. + */ private Map<InputKey.SpecialKey, KeyAction> specialActionKeys = new LinkedHashMap<InputKey.SpecialKey, KeyAction>(); - private boolean paint = true; + /** + * The programm runs while this var is set to true. + * All threads should stop when this is set to false + */ private boolean run = true; // set to false on quit /** @@ -370,10 +382,13 @@ output("Welcome to the SQLShell client."); output("Type 'help' to get a list of available commands other then the default sql input."); + // + // Thread for executing the sql commands in the background + // Runnable r = new Runnable() { @Override public void run() { - while ( run ) { + while ( isRunning() ) { final CommandInfo cmd; try { cmd = commandQueue.poll(5, TimeUnit.SECONDS); @@ -408,7 +423,6 @@ }; commandThread.start(); } - } } }; @@ -418,24 +432,45 @@ } - // TODO move to Screen??? + /** + * Return the visible screen width. + * @return the visible screen width. + */ public abstract int getScreenWidth(); + /** + * Return the visible screen height. + * @return the visible screen height. + */ public abstract int getScreenHeight(); + /** + * Repaint the screen. + */ protected void repaint() { - if (paint) { - try { - synchronized(this) { - paint(screen); - } - } catch(Throwable t) { - error(t); - } + if (isRunning()) { + paint(getScreen()); } } + + /** + * Paints the screen. + * @param screen the screen to be painted + */ public abstract void paint(Screen screen); + /** + * Make a beep sound. + */ public abstract void beep(); + /** + * Print a debug string. + * @param debug the string to print in the debug line + */ public abstract void debug(String debug); + /** + * Shows a popupwindow to allow the user to make a selection from the list of possible matches. + * @param items the list of possible matches + * @param p the character x/y location to show the popupscreen on + */ public abstract String select(List<String> items, Point p); /** * Returns the connection to the database @@ -445,9 +480,10 @@ return DBConnector.getInstance().getConnection(); } - public void close() { - debug("CLOSE HERE"); - System.out.println("CLOSE HERE"); + /** + * Close all writers. + */ + public void close() { // TODO move to someplace else where it's less likely to forget if (spoolWriter != null) { try { spoolWriter.close(); @@ -1111,7 +1147,6 @@ } @Override public CharSequence execute(SQLCommand command) { - paint = false; run = false; close(); return "Application terminated."; @@ -1771,6 +1806,15 @@ cursorPosition.x = lineBuffer.length(); lineBuffer.append(line); } + + protected boolean isRunning() { + return run; + } + + protected Screen getScreen() { + return screen; + } + private interface KeyAction { void execute(); CharSequence getHelp(); Modified: src/main/java/nl/improved/sqlclient/Screen.java =================================================================== --- src/main/java/nl/improved/sqlclient/Screen.java 2008-07-31 07:59:52 UTC (rev 276) +++ src/main/java/nl/improved/sqlclient/Screen.java 2008-08-02 10:11:20 UTC (rev 277) @@ -19,7 +19,7 @@ import nl.improved.sqlclient.util.LimitedArrayList; /** - * + * Screen class holding information that needs to be painted in the sql window. * @author roy */ public class Screen { @@ -28,6 +28,9 @@ * The (default) maximum matches to show in a selection dialog. */ public static final int MAX_MATCH_SIZE = 15; + /** + * The maximum line length. + */ public int MAX_LINE_LENGTH; // not static and final.. since it can change on window resize /** * The prompt to show when entering sql commands. @@ -60,35 +63,63 @@ */ private Point cursorPosition = new Point(0,0); + /** + * Returns the position of the blinking cursor. + * @return the position of the blinking cursor. + */ public Point getCursorPosition() { return cursorPosition; } + /** + * Returns the screen buffer. + * @return the screen buffer. + */ public List<CharSequence> getScreenBuffer() { return screenBuffer; } + /** + * Returns the pageup count used for viewing history. + * @return the pageup count used for viewing history. + */ public int getPageUpCount() { return pageUpCount; } + /** + * Changes the pageup count used for viewing history. + * @param pageUpCount the pageup count used for viewing history. + */ public void setPageUpCount(int pageUpCount) { this.pageUpCount = pageUpCount; } + /** + * Returns true if the prompt should be visible. + * @return true if the prompt should be visible. + */ public boolean getShowPrompt() { return showPrompt; } + /** + * Set to true if the prompt should be visible. + * @param showPrompt true if the prompt should be visible. + */ public void setShowPrompt(boolean showPrompt) { this.showPrompt = showPrompt; } + /** + * Returns a full empty line string. + * @return a full empty line string. + */ public String getEmptyLine() { return emptyLine; } - public void setEmptyLine(String emptyLine) { + void setEmptyLine(String emptyLine) { this.emptyLine = emptyLine; } } Modified: src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java =================================================================== --- src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java 2008-07-31 07:59:52 UTC (rev 276) +++ src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java 2008-08-02 10:11:20 UTC (rev 277) @@ -32,27 +32,43 @@ import nl.improved.sqlclient.commands.Command; /** - * + * SQLShell window based on the jcurses library. + * @see http://jcurses.sourceforge.net/ * @author roy */ public class SQLShellWindow extends AbstractSQLShellWindow { private Window window; + private boolean dontRepaint = false; + private boolean repaint = true; + /** + * Default constructor. + */ public SQLShellWindow() { - //commands.register("WINDOW[\\s]+[A-Z]+", new WindowCommand()); } - + /** + * Return the screen width. + * @return the screen width. + */ @Override public int getScreenWidth() { return Toolkit.getScreenWidth(); } + /** + * Return the screen height. + * @return the screen height. + */ @Override public int getScreenHeight() { return Toolkit.getScreenHeight(); } + /** + * Write a debug string. + * @param debug the debug to output + */ @Override public void debug(String debug) { synchronized(this) { @@ -62,6 +78,9 @@ } + /** + * Set the window to be visible. + */ public void show() { if (window == null) { window = new Window(getScreenWidth(), getScreenHeight(), true, "SQLShell") { @@ -116,8 +135,26 @@ }; } window.setVisible(true); + while (isRunning()) { + Screen screen = getScreen(); + if (!dontRepaint && repaint) { + try { + synchronized(this) { + paint(screen); + } + } catch(Throwable t) { + error(t); + } + } + try { + Thread.sleep(200); + } catch(Exception e) {} + } } + /** + * Close the window. + */ @Override public void close() { debug("CLOSE CALLED"); @@ -125,61 +162,32 @@ window.close(); } + /** - * Some basic window settings like resize. + * Shows a popupwindow to allow the user to make a selection from the list of possible matches. + * @param items the list of possible matches + * @param p the character x/y location to show the popupscreen on */ - private class WindowCommand implements Command { - @Override - public CharSequence execute(SQLCommand cmd) { - String command = cmd.getCommandString(); - String argument = command.trim().substring("window".length()).trim(); - if (argument.equalsIgnoreCase("resize")) { - //window.resize(getScreenWidth(),getScreenHeight()); // TODO - return "Window resized to " + getScreenWidth() +"x"+getScreenHeight(); - } - return "Uknown command '"+ argument+"'"; - } - - @Override - public CharSequence getCommandString() { - return "window"; - } - - /** - * Returns some tab completion info for the specified command. - * @param commandInfo the command lines - * @param commandPoint the cursor position - * @return some tab completion info for the specified command. - */ - @Override - public TabCompletionInfo getTabCompletionInfo(SQLCommand command, Point commandPoint) { - return null; - } - @Override - public CharSequence getHelp() { - return "window resize: notify the sql client of a screen resize"; - } - @Override - public boolean abort() { - return false;// not implemented - } - @Override - public boolean backgroundProcessSupported() { - return false; - } - } - @Override public String select(List<String> items, Point p) { - PopUpMenu menu = new PopUpMenu(p.x,Math.max(2, p.y-items.size()), "Find match"); - Iterator<String> iMatches = items.iterator(); - while (iMatches.hasNext()) { - menu.add(iMatches.next()); + try { + PopUpMenu menu = new PopUpMenu(p.x,Math.max(2, p.y-items.size()), "Find match"); + Iterator<String> iMatches = items.iterator(); + while (iMatches.hasNext()) { + menu.add(iMatches.next()); + } + dontRepaint = true; + menu.show(); + return menu.getSelectedItem(); + } finally { + dontRepaint = false; } - menu.show(); - return menu.getSelectedItem(); } + /** + * Paint the screen. + * @param screen the screen information to be painted + */ @Override public void paint(Screen screen) { synchronized(this) { @@ -240,6 +248,24 @@ } } + /** + * Override to allow painting on the main thread to overcome painting errors. + */ + @Override + protected void repaint() { + if (isRunning()) { + repaint = true; + } + } + + /** + * Beep. + */ + @Override + public void beep() { + Toolkit.beep(); + } + public static void main(String[] args) { SQLShellWindow shell = new SQLShellWindow(); shell.show(); @@ -250,9 +276,4 @@ } */ } - - @Override - public void beep() { - Toolkit.beep(); - } } |
From: SVN by r. <sv...@ca...> - 2008-08-03 11:37:46
|
Author: roy Date: 2008-08-03 13:37:34 +0200 (Sun, 03 Aug 2008) New Revision: 278 Modified: src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java Log: improved help of dump/read commands code cleanup Modified: src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java =================================================================== --- src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java 2008-08-02 10:11:20 UTC (rev 277) +++ src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java 2008-08-03 11:37:34 UTC (rev 278) @@ -1233,11 +1233,13 @@ StringBuilder returnValue = new StringBuilder(); returnValue.append("Available key mappings:\n"); int max = 0; - Iterator<KeyAction> iKeyActions = actionKeys.values().iterator(); + List<KeyAction> keys = new ArrayList<KeyAction>(actionKeys.values()); + keys.addAll(0, specialActionKeys.values()); + Iterator<KeyAction> iKeyActions = keys.iterator(); while (iKeyActions.hasNext()) { max = Math.max(max, iKeyActions.next().getHelp().toString().indexOf('\t')); } - iKeyActions = actionKeys.values().iterator(); + iKeyActions = keys.iterator(); while (iKeyActions.hasNext()) { returnValue.append(" "); //returnValue.append(iKeyActions.next().getHelp()); @@ -1390,6 +1392,7 @@ } else { dumpFileName = nextPart; } + int rowCount = 0; try { File f = new File(toFileName(dumpFileName +".dmp")); fileName = f.getAbsolutePath(); @@ -1423,6 +1426,7 @@ dumpWriter.write("</col>\n"); } dumpWriter.write(" </row>\n"); + rowCount++; } dumpWriter.write("</dump>"); } catch (SQLException e) { @@ -1436,7 +1440,7 @@ Logger.getLogger(AbstractSQLShellWindow.class.getName()).log(Level.SEVERE, null, ex); } } - return "Dump to "+fileName+" done."; + return "Dump to "+fileName+" done. ("+ rowCount+" rows written)"; } @Override @@ -1456,7 +1460,9 @@ } @Override public CharSequence getHelp() { - return "tablename [where clause]"; + return "tablename [where clause]\n" + + "For example: dump users where role='manager';\n\n" + + "See 'read' for options to read the dump file back into the table"; } @Override public boolean abort() { @@ -1483,6 +1489,7 @@ } else { dumpFileName = nextPart; } + int rowCount = 0; try { File f = new File(toFileName(dumpFileName +".dmp")); fileName = f.getAbsolutePath(); @@ -1506,7 +1513,6 @@ String values = ") values ("; Element n = (Element) nodeList.item(0); // row NodeList cols = n.getElementsByTagName("col"); - output("LENGTH : "+ n.getNodeName() +"/ "+ cols.getLength()); for (int colNr = 0; colNr < cols.getLength(); colNr++) { query += cols.item(colNr).getAttributes().getNamedItem("name").getNodeValue(); values += "?"; @@ -1538,6 +1544,7 @@ } } pstmt.executeUpdate(); + rowCount++; } } catch (SQLException e) { throw new IllegalStateException("Failed to execute update query for dump("+fileName+"): " + e.toString(), e); @@ -1546,7 +1553,7 @@ } catch (Exception e) { throw new IllegalStateException("Failed to read dump ("+fileName+"): " + e.toString(), e); } - return "Read from "+fileName+" done."; + return "Read from "+fileName+" done. (" + rowCount+" rows imported)"; } @Override @@ -1566,7 +1573,8 @@ } @Override public CharSequence getHelp() { - return "filename: read dump file from filename\n"; + return "filename: read dump file from filename\n"+ + "See 'dump' for options to create a dump file"; } @Override public boolean abort() { Modified: src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java =================================================================== --- src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java 2008-08-02 10:11:20 UTC (rev 277) +++ src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java 2008-08-03 11:37:34 UTC (rev 278) @@ -26,10 +26,7 @@ import nl.improved.sqlclient.AbstractSQLShellWindow; import nl.improved.sqlclient.InputKey; import nl.improved.sqlclient.Point; -import nl.improved.sqlclient.SQLCommand; import nl.improved.sqlclient.Screen; -import nl.improved.sqlclient.TabCompletionInfo; -import nl.improved.sqlclient.commands.Command; /** * SQLShell window based on the jcurses library. @@ -135,6 +132,7 @@ }; } window.setVisible(true); + // repaint loop while (isRunning()) { Screen screen = getScreen(); if (!dontRepaint && repaint) { @@ -157,7 +155,6 @@ */ @Override public void close() { - debug("CLOSE CALLED"); super.close(); window.close(); } |
From: SVN by r. <sv...@ca...> - 2008-08-03 20:03:09
|
Author: roy Date: 2008-08-03 22:03:00 +0200 (Sun, 03 Aug 2008) New Revision: 281 Modified: src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java src/main/java/nl/improved/sqlclient/DBConnector.java src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java Log: made connector not dependant on jcurses.. added initial implementation for dump binary data Modified: src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java =================================================================== --- src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java 2008-08-03 20:01:43 UTC (rev 280) +++ src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java 2008-08-03 20:03:00 UTC (rev 281) @@ -53,6 +53,7 @@ import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.sax.TransformerHandler; import javax.xml.transform.stream.StreamResult; +import nl.improved.sqlclient.DBConnector.ConnectionSettings; import nl.improved.sqlclient.commands.*; import nl.improved.sqlclient.util.LimitedArrayList; import org.w3c.dom.Document; @@ -61,6 +62,7 @@ import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; +import sun.misc.BASE64Encoder; /** * The SQLShell abstract main class. @@ -481,6 +483,8 @@ * @param p the character x/y location to show the popupscreen on */ public abstract String select(List<String> items, Point p); + + protected abstract String[] getLoginCredentials(String username, String password) throws SQLException; /** * Returns the connection to the database * @return the connection to the database @@ -978,7 +982,7 @@ /** * Connect command for setting up a connection to a database. */ - private static class ConnectCommand implements Command { + private class ConnectCommand implements Command { /** * Execute the connection command and return a readable result. * @param command the command string for setting up a connection @@ -992,8 +996,11 @@ if (cmdString.length() > 0 && cmdString.charAt(cmdString.length()-1) == ';') { cmdString = cmdString.substring(0, cmdString.length()-1); } - DBConnector.getInstance().connect(cmdString); - return "Connected.\n\n"; + if (connect(cmdString) != null) { + return "Connected.\n\n"; + } else { + return "Connect failed. Unknown reason\n\n"; + } } catch(SQLException e) { throw new IllegalStateException("Failed to connect: " + e.getMessage(), e); } @@ -1057,6 +1064,88 @@ public boolean backgroundProcessSupported() { return false; } + + /** + * Connects to a database, using a connection string. + * + * Possible cases: + * <table> + * <tr> + * <td>"user/pass@ident"</td> + * <td>Connects to ident as user with password pass</td> + * </tr> + * <tr> + * <td>"user/@ident"</td> + * <td>Connects to ident connection as user with empty password</td> + * </tr> + * <tr> + * <td>"user@ident"</td> + * <td>Connects to ident connection as user, without specifying a password (will be prompted for pass)</td> + * </tr> + * <tr> + * <td>"@ident"</td> + * <td>Connects to ident connection, without specifying user or password. If default user is specified that will be used, if not: will be prompted for user/pass</td> + * </tr> + * <tr> + * <td>"user/pass"</td> + * <td>Connects to default connection as user, with password pass</td> + * </tr> + * <tr> + * <td>"user/"</td> + * <td>Connects to default connection as user, with empty password</td> + * </tr> + * <tr> + * <td>"user"</td> + * <td>Connects to default connection as user, without specifying a password (will be prompted for pass)</td> + * </tr> + * <tr> + * <td>""</td> + * <td>Connects to default connection, without specifying user or password. If default user is specified that will be used, if not: will be prompted for user/pass</td> + * </tr> + * <table> + * + * @param connectString the connect string, following the conditions as specified above. + * @return the opened Connection + * @throws SQLException if the connection could not be opened. + */ + public Connection connect(String connectString) throws SQLException { + //Shortcut - no need to try and parse everything if we already know we're empty + DBConnector dbConnector = DBConnector.getInstance(); + String ident = dbConnector.getDefaultIdentifier(); + String username = null; + String password = null; + if (connectString != null && connectString.trim().length() > 0) { + + String[] connectParts = connectString.split("@"); + if (connectParts.length > 1) { + ident = connectParts[1].trim(); + } + + if (connectParts[0].matches(".+/.*")) { + String[] loginDetails = connectParts[0].split("/"); + if (loginDetails.length > 1) { + password = loginDetails[1]; + } else { + password = ""; + } + username = loginDetails[0].trim(); + } else { + username = connectParts[0].trim(); + } + } + if (username == null || password == null) { + ConnectionSettings predefinedSettings = dbConnector.getPredefinedConnectionSettings(ident); + if (predefinedSettings != null) { + username = predefinedSettings.getUsername(); + password = predefinedSettings.getPassword(); + } + String[] credentials = getLoginCredentials(username, password); + username = credentials[0]; + password = credentials[1]; + } + + return dbConnector.connect(ident, username, password); + } } /** * Command that enables the user to close a connection. @@ -1415,7 +1504,7 @@ TransformerHandler hd = tf.newTransformerHandler(); Transformer serializer = hd.getTransformer(); serializer.setOutputProperty(OutputKeys.ENCODING,"ISO-8859-1"); - serializer.setOutputProperty(OutputKeys.INDENT,"yes"); + //serializer.setOutputProperty(OutputKeys.INDENT,"no"); hd.setResult(streamResult); hd.startDocument(); AttributesImpl atts = new AttributesImpl(); @@ -1445,12 +1534,34 @@ } else if (metaData.getColumnType(col) == Types.BINARY || metaData.getColumnType(col) == Types.BLOB || metaData.getColumnType(col) == Types.CLOB || - metaData.getColumnType(col) == Types.LONGNVARCHAR) { - InputStream valueStream = rs.getBinaryStream(col); - //Base64 + metaData.getColumnType(col) == Types.LONGNVARCHAR || + metaData.getColumnType(col) == Types.LONGVARBINARY || + metaData.getColumnType(col) == Types.LONGVARCHAR || + metaData.getColumnType(col) == Types.NCLOB || + metaData.getColumnType(col) == Types.OTHER) { atts.addAttribute("","","type","","binary"); hd.startElement("","","col",atts); + + BASE64Encoder enc = new BASE64Encoder(); + int bytesSize = 128; + byte[] bytes = new byte[bytesSize]; + int read; + InputStream valueStream = rs.getBinaryStream(col); + while ( (read = valueStream.read(bytes)) != -1) { + if (read == 0) { + continue; + } + if (read != bytes.length) { + bytes = Arrays.copyOf(bytes, read); + } + String stringValue = enc.encode(bytes) +"\n"; + hd.characters(stringValue.toCharArray(), 0, stringValue.length()); + if (bytes.length != bytesSize) { + bytes = new byte[bytesSize]; + } + } } else { + atts.addAttribute("","","type","",metaData.getColumnTypeName(col)); hd.startElement("","","col",atts); String value= rs.getString(col); if (value != null) { Modified: src/main/java/nl/improved/sqlclient/DBConnector.java =================================================================== --- src/main/java/nl/improved/sqlclient/DBConnector.java 2008-08-03 20:01:43 UTC (rev 280) +++ src/main/java/nl/improved/sqlclient/DBConnector.java 2008-08-03 20:03:00 UTC (rev 281) @@ -27,20 +27,9 @@ import java.util.Set; import java.util.Map; import java.util.Properties; -import jcurses.system.InputChar; -import jcurses.event.ActionEvent; -import jcurses.event.ActionListener; -import jcurses.widgets.Button; -import jcurses.widgets.Dialog; -import jcurses.widgets.GridLayoutManager; -import jcurses.widgets.Label; -import jcurses.widgets.PasswordField; -import jcurses.widgets.TextField; -import jcurses.widgets.WidgetsConstants; public final class DBConnector { - /** * The default formatting pattern for Date or Date-like columns. */ @@ -119,79 +108,7 @@ return null; } - /** - * Connects to a database, using a connection string. - * - * Possible cases: - * <table> - * <tr> - * <td>"user/pass@ident"</td> - * <td>Connects to ident as user with password pass</td> - * </tr> - * <tr> - * <td>"user/@ident"</td> - * <td>Connects to ident connection as user with empty password</td> - * </tr> - * <tr> - * <td>"user@ident"</td> - * <td>Connects to ident connection as user, without specifying a password (will be prompted for pass)</td> - * </tr> - * <tr> - * <td>"@ident"</td> - * <td>Connects to ident connection, without specifying user or password. If default user is specified that will be used, if not: will be prompted for user/pass</td> - * </tr> - * <tr> - * <td>"user/pass"</td> - * <td>Connects to default connection as user, with password pass</td> - * </tr> - * <tr> - * <td>"user/"</td> - * <td>Connects to default connection as user, with empty password</td> - * </tr> - * <tr> - * <td>"user"</td> - * <td>Connects to default connection as user, without specifying a password (will be prompted for pass)</td> - * </tr> - * <tr> - * <td>""</td> - * <td>Connects to default connection, without specifying user or password. If default user is specified that will be used, if not: will be prompted for user/pass</td> - * </tr> - * <table> - * - * @param connectString the connect string, following the conditions as specified above. - * @return the opened Connection - * @throws SQLException if the connection could not be opened. - */ - public Connection connect(String connectString) throws SQLException { - //Shortcut - no need to try and parse everything if we already know we're empty - if (connectString == null || connectString.trim().length() == 0) { - return connect(defaultConnector, null, null); - } - String ident = defaultConnector; - String username = null; - String password = null; - - String[] connectParts = connectString.split("@"); - if (connectParts.length > 1) { - ident = connectParts[1].trim(); - } - - if (connectParts[0].matches(".+/.*")) { - String[] loginDetails = connectParts[0].split("/"); - if (loginDetails.length > 1) { - password = loginDetails[1]; - } else { - password = ""; - } - username = loginDetails[0].trim(); - } else { - username = connectParts[0].trim(); - } - - return connect(getPredefinedConnectionSettings(ident), username, password); - } - public QueryExecutor getQueryExecutor() { if (queryExecutor == null) { if (dateIsTimeStamp) { @@ -203,7 +120,7 @@ return queryExecutor; } - private ConnectionSettings getPredefinedConnectionSettings(String identifier) { + public ConnectionSettings getPredefinedConnectionSettings(String identifier) { if (predefinedConnections.containsKey(identifier)) { return predefinedConnections.get(identifier); } @@ -271,16 +188,6 @@ } } - if (username == null || password == null) { - LoginDialog ld = new LoginDialog(username, password); - ld.show(); - if (!ld.exitOk) { - throw new SQLException("Connect cancelled."); - } - username = ld.getUsername(); - password = ld.getPassword(); - } - activeConnection = DriverManager.getConnection(settings.getConnectionURL(), username, password); activeConnection.setAutoCommit(autoCommit); // INITIALIZE database settings @@ -342,7 +249,7 @@ return statement; } - private static class ConnectionSettings { + public static class ConnectionSettings { private String identifier; private String connectionURL; private String driver; @@ -386,100 +293,4 @@ return password; } } - - private static class LoginDialog extends Dialog { - private boolean exitOk = false; - private TextField userfield; - private PasswordField passfield; - - public LoginDialog(final String username, final String password) { - super(10,10, 50, 7, true,"Connect"); - userfield = new TextField(); - setUsername(username); - passfield = new PasswordField() { - - @Override - protected void focus() { - super.focus(); - } - - protected boolean handleInput(InputChar ch) { - if (!ch.isSpecialCode() && ch.getCharacter() == '\n') { - okButtonPressedSlot(); - return false; - } - return super.handleInput(ch); - } - }; - setPassword(password); - - Button okButton = new Button("Ok"); - okButton.addListener(new ActionListener() { - public void actionPerformed(ActionEvent event) { - okButtonPressedSlot(); - } - }); - Button cancelButton = new Button("Cancel"); - cancelButton.addListener(new ActionListener() { - public void actionPerformed(ActionEvent arg0) { - LoginDialog.this.exitOk = false; - LoginDialog.this.close(); - } - }); - - GridLayoutManager glm = new GridLayoutManager(4,3); - getRootPanel().setLayoutManager(glm); - - glm.addWidget(new Label("Username"), 0,0,1,1, WidgetsConstants.ALIGNMENT_CENTER, WidgetsConstants.ALIGNMENT_LEFT); - glm.addWidget(userfield, 1,0,3,1, WidgetsConstants.ALIGNMENT_CENTER, WidgetsConstants.ALIGNMENT_LEFT); - glm.addWidget(new Label("Password"), 0,1,1,1, WidgetsConstants.ALIGNMENT_CENTER, WidgetsConstants.ALIGNMENT_LEFT); - glm.addWidget(passfield, 1,1,3,1, WidgetsConstants.ALIGNMENT_CENTER, WidgetsConstants.ALIGNMENT_LEFT); - - glm.addWidget(okButton, 1,2,1,1, WidgetsConstants.ALIGNMENT_CENTER, WidgetsConstants.ALIGNMENT_CENTER); - glm.addWidget(cancelButton, 2,2,1,1, WidgetsConstants.ALIGNMENT_CENTER, WidgetsConstants.ALIGNMENT_CENTER); - - } - public void okButtonPressedSlot() { - exitOk = true; - close(); - } - - @Override - protected void activate() { - super.activate(); - - if (userfield.getText().length() == 0) { - userfield.getFocus(); - } else if (passfield.getText().length() == 0) { - passfield.getFocus(); - } else { - throw new IllegalStateException("We have login data, but get a login dailog anyway."); - } - } - - public void setUsername(String username) { - if (username == null) { - userfield.setText(""); - } else { - userfield.setText(username); - } - } - - public String getUsername() { - return userfield.getText(); - } - - public void setPassword(String password) { - if (password == null) { - passfield.setText(""); - } else { - passfield.setText(password); - } - } - - public String getPassword() { - return passfield.getText(); - } - } - } Modified: src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java =================================================================== --- src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java 2008-08-03 20:01:43 UTC (rev 280) +++ src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java 2008-08-03 20:03:00 UTC (rev 281) @@ -15,13 +15,23 @@ */ package nl.improved.sqlclient.jcurses; +import java.sql.SQLException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import jcurses.event.ActionEvent; +import jcurses.event.ActionListener; import jcurses.system.CharColor; import jcurses.system.InputChar; import jcurses.system.Toolkit; +import jcurses.widgets.Button; +import jcurses.widgets.Dialog; +import jcurses.widgets.GridLayoutManager; +import jcurses.widgets.Label; +import jcurses.widgets.PasswordField; import jcurses.widgets.PopUpMenu; +import jcurses.widgets.TextField; +import jcurses.widgets.WidgetsConstants; import jcurses.widgets.Window; import nl.improved.sqlclient.AbstractSQLShellWindow; import nl.improved.sqlclient.InputKey; @@ -245,6 +255,21 @@ } } + @Override + protected String[] getLoginCredentials(String username, String password) throws SQLException { + LoginDialog diag = new LoginDialog(username, password); + dontRepaint = true; + try { + diag.show(); + if (!diag.exitOk) { + throw new SQLException("Connect cancelled."); + } + return new String[] {diag.getUsername(), diag.getPassword() }; + } finally { + dontRepaint = false; + } + } + /** * Override to allow painting on the main thread to overcome painting errors. */ @@ -263,6 +288,104 @@ Toolkit.beep(); } + private static class LoginDialog extends Dialog { + private boolean exitOk = false; + private TextField userfield; + private PasswordField passfield; + + public LoginDialog(final String username, final String password) { + super(10,10, 50, 7, true,"Connect"); + userfield = new TextField(); + setUsername(username); + passfield = new PasswordField() { + + @Override + protected void focus() { + super.focus(); + } + + @Override + protected boolean handleInput(InputChar ch) { + if (!ch.isSpecialCode() && ch.getCharacter() == '\n') { + okButtonPressedSlot(); + return false; + } + return super.handleInput(ch); + } + }; + setPassword(password); + + Button okButton = new Button("Ok"); + okButton.addListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + okButtonPressedSlot(); + } + }); + Button cancelButton = new Button("Cancel"); + cancelButton.addListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + LoginDialog.this.exitOk = false; + LoginDialog.this.close(); + } + }); + + GridLayoutManager glm = new GridLayoutManager(4,3); + getRootPanel().setLayoutManager(glm); + + glm.addWidget(new Label("Username"), 0,0,1,1, WidgetsConstants.ALIGNMENT_CENTER, WidgetsConstants.ALIGNMENT_LEFT); + glm.addWidget(userfield, 1,0,3,1, WidgetsConstants.ALIGNMENT_CENTER, WidgetsConstants.ALIGNMENT_LEFT); + glm.addWidget(new Label("Password"), 0,1,1,1, WidgetsConstants.ALIGNMENT_CENTER, WidgetsConstants.ALIGNMENT_LEFT); + glm.addWidget(passfield, 1,1,3,1, WidgetsConstants.ALIGNMENT_CENTER, WidgetsConstants.ALIGNMENT_LEFT); + + glm.addWidget(okButton, 1,2,1,1, WidgetsConstants.ALIGNMENT_CENTER, WidgetsConstants.ALIGNMENT_CENTER); + glm.addWidget(cancelButton, 2,2,1,1, WidgetsConstants.ALIGNMENT_CENTER, WidgetsConstants.ALIGNMENT_CENTER); + + } + public void okButtonPressedSlot() { + exitOk = true; + close(); + } + + @Override + protected void activate() { + super.activate(); + + if (userfield.getText().length() == 0) { + userfield.getFocus(); + } else if (passfield.getText().length() == 0) { + passfield.getFocus(); + } else { + throw new IllegalStateException("We have login data, but get a login dailog anyway."); + } + } + + public void setUsername(String username) { + if (username == null) { + userfield.setText(""); + } else { + userfield.setText(username); + } + } + + public String getUsername() { + return userfield.getText(); + } + + public void setPassword(String password) { + if (password == null) { + passfield.setText(""); + } else { + passfield.setText(password); + } + } + + public String getPassword() { + return passfield.getText(); + } + } + public static void main(String[] args) { SQLShellWindow shell = new SQLShellWindow(); shell.show(); |
From: SVN by r. <sv...@ca...> - 2008-08-06 14:23:37
|
Author: roy Date: 2008-08-06 16:23:23 +0200 (Wed, 06 Aug 2008) New Revision: 283 Modified: src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java Log: small code cleanup Modified: src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java =================================================================== --- src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java 2008-08-05 12:16:24 UTC (rev 282) +++ src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java 2008-08-06 14:23:23 UTC (rev 283) @@ -77,7 +77,7 @@ /** * The current command lines. */ - protected SQLCommand commandLines; + private SQLCommand commandLines; /** * All commands in history. */ @@ -518,6 +518,15 @@ return commandLines; } + protected List<SQLCommand> getUnprocessedCommands() { + List<SQLCommand> returnValue = new ArrayList<SQLCommand>(); + Iterator<CommandInfo> i = commandQueue.iterator(); + while (i.hasNext()) { + returnValue.add(i.next().cmd); + } + return returnValue; + } + /** * Add a new line to the command lines buffer. */ Modified: src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java =================================================================== --- src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java 2008-08-05 12:16:24 UTC (rev 282) +++ src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java 2008-08-06 14:23:23 UTC (rev 283) @@ -39,6 +39,7 @@ import nl.improved.sqlclient.InputKey; import nl.improved.sqlclient.Point; import nl.improved.sqlclient.Screen; +import nl.improved.sqlclient.SQLCommand; /** * SQLShell window based on the jcurses library. @@ -230,6 +231,7 @@ tmpList.addAll(screenBuffer); //add prompt + SQLCommand commandLines = getCommand(); List<? extends CharSequence> currentLines = commandLines.getLines(); for (int i = 0; i < currentLines.size(); i++) { if (i == 0 && screen.getShowPrompt()) { |
From: SVN by r. <sv...@ca...> - 2008-08-06 20:33:01
|
Author: roy Date: 2008-08-06 22:32:52 +0200 (Wed, 06 Aug 2008) New Revision: 284 Modified: src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java Log: fixes show 'queries in progress' that will be executed once the current query is done Modified: src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java =================================================================== --- src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java 2008-08-06 14:23:23 UTC (rev 283) +++ src/main/java/nl/improved/sqlclient/AbstractSQLShellWindow.java 2008-08-06 20:32:52 UTC (rev 284) @@ -522,7 +522,7 @@ List<SQLCommand> returnValue = new ArrayList<SQLCommand>(); Iterator<CommandInfo> i = commandQueue.iterator(); while (i.hasNext()) { - returnValue.add(i.next().cmd); + returnValue.add(i.next().sql); } return returnValue; } Modified: src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java =================================================================== --- src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java 2008-08-06 14:23:23 UTC (rev 283) +++ src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java 2008-08-06 20:32:52 UTC (rev 284) @@ -231,14 +231,18 @@ tmpList.addAll(screenBuffer); //add prompt - SQLCommand commandLines = getCommand(); - List<? extends CharSequence> currentLines = commandLines.getLines(); - for (int i = 0; i < currentLines.size(); i++) { - if (i == 0 && screen.getShowPrompt()) { - tmpList.add(Screen.PROMPT+"> "+currentLines.get(i)); - } else { - String nrI = Integer.toString(i+1); - tmpList.add(screen.getEmptyLine().substring(0,Screen.PROMPT.length() - nrI.length()) + nrI+"> "+currentLines.get(i)); + List<SQLCommand> commands = getUnprocessedCommands(); + commands.add(getCommand()); + for (SQLCommand commandLines : commands) { + //SQLCommand commandLines = getCommand(); + List<? extends CharSequence> currentLines = commandLines.getLines(); + for (int i = 0; i < currentLines.size(); i++) { + if (i == 0 && screen.getShowPrompt()) { + tmpList.add(Screen.PROMPT+"> "+currentLines.get(i)); + } else { + String nrI = Integer.toString(i+1); + tmpList.add(screen.getEmptyLine().substring(0,Screen.PROMPT.length() - nrI.length()) + nrI+"> "+currentLines.get(i)); + } } } int startLine; @@ -268,6 +272,7 @@ color = new CharColor(CharColor.BLACK, CharColor.WHITE, CharColor.REVERSE, CharColor.REVERSE); String cursorChar = " "; Point cursorPosition = screen.getCursorPosition(); + SQLCommand commandLines = getCommand(); if (commandLines.getLines().size() > 0) { String tmp = commandLines.getLines().get(cursorPosition.y).toString(); if (cursorPosition.x < 0) { |
From: SVN by r. <sv...@ca...> - 2009-01-25 14:19:21
|
Author: roy Date: 2009-01-25 15:19:15 +0100 (Sun, 25 Jan 2009) New Revision: 368 Modified: src/main/java/nl/improved/sqlclient/ src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java Log: jcurses paint fixes Property changes on: src/main/java/nl/improved/sqlclient ___________________________________________________________________ Name: svn:ignore - .SQLPlusPlus.java.swp .SQLPlus.java.swp .DBConnector.java.swp .SQLUtil.java.swp .Point.java.swp .SQLLineWrapper.java.swp .SQLOutput.java.swp .QueryExecutor.java.swp .SQLShell.java.swp .AbstractSQLShellWindow.java.swo .AbstractSQLShellWindow.java.swp .TabCompletionInfo.java.swp + .SQLPlusPlus.java.swp .SQLPlus.java.swp .DBConnector.java.swp .SQLUtil.java.swp .Point.java.swp .SQLLineWrapper.java.swp .SQLOutput.java.swp .QueryExecutor.java.swp .SQLShell.java.swp .AbstractSQLShellWindow.java.swo .AbstractSQLShellWindow.java.swp .TabCompletionInfo.java.swp .Screen.java.swp Modified: src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java =================================================================== --- src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java 2009-01-25 14:00:26 UTC (rev 367) +++ src/main/java/nl/improved/sqlclient/jcurses/SQLShellWindow.java 2009-01-25 14:19:15 UTC (rev 368) @@ -170,7 +170,7 @@ } } - textComponent.setText(newText.toString(), true); + textComponent.setText(newText.toString(), false); new Thread() { @Override @@ -328,21 +328,16 @@ } public void draw() { if (isVisible()) { - Point cursorPos = getScreen().getCursorPosition(); - setCursorLocation(cursorPos.x, cursorPos.y); - //doPaint(); - focus(); // repaints and paints cursor + doRepaint(); } } @Override protected boolean handleInput(InputChar arg0) { - //return super.handleInput(arg0); - //debug("handle input: "+ toInputKey(arg0)); SQLShellWindow.this.handleInput(toInputKey(arg0)); return true; } - } + } } |