[Mulgara-svn] r434 - branches/nw-interface/src/jar/itql/java/org/mulgara/itql
pag at mulgara.org
pag at mulgara.org
Thu Sep 13 20:37:12 UTC 2007
Author: pag
Date: 2007-09-13 15:37:11 -0500 (Thu, 13 Sep 2007)
New Revision: 434
Added:
branches/nw-interface/src/jar/itql/java/org/mulgara/itql/CommandSplitter.java
branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlAutoInterpreter.java
branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlCommandSplitter.java
branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlSession.java
branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlSessionUI.java
Modified:
branches/nw-interface/src/jar/itql/java/org/mulgara/itql/ItqlInterpreter.java
branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlInterpreter.java
Log:
Starting to reimplement the front end user tools, to use the new parser class. Preliminary work at this point.
Added: branches/nw-interface/src/jar/itql/java/org/mulgara/itql/CommandSplitter.java
===================================================================
--- branches/nw-interface/src/jar/itql/java/org/mulgara/itql/CommandSplitter.java 2007-09-13 11:23:14 UTC (rev 433)
+++ branches/nw-interface/src/jar/itql/java/org/mulgara/itql/CommandSplitter.java 2007-09-13 20:37:11 UTC (rev 434)
@@ -0,0 +1,31 @@
+/**
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.opensource.org/licenses/osl-3.0.txt
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ */
+package org.mulgara.itql;
+
+import java.util.List;
+
+/**
+ * This interface splits queries for the appropriate query type
+ * @created Sep 11, 2007
+ * @author Paul Gearon
+ * @copyright © 2007 <a href="mailto:gearon at ieee.org">Paul Gearon</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+interface CommandSplitter {
+
+ /**
+ * Split the given string into an array of strings containing individual elements.
+ * @param All the commands in a long string.
+ * @return An array of individual commands.
+ */
+ List<String> split(String commands);
+}
Modified: branches/nw-interface/src/jar/itql/java/org/mulgara/itql/ItqlInterpreter.java
===================================================================
--- branches/nw-interface/src/jar/itql/java/org/mulgara/itql/ItqlInterpreter.java 2007-09-13 11:23:14 UTC (rev 433)
+++ branches/nw-interface/src/jar/itql/java/org/mulgara/itql/ItqlInterpreter.java 2007-09-13 20:37:11 UTC (rev 434)
@@ -572,7 +572,7 @@
* @throws IOException if the <var>command</var> cannot be paersed
* @throws IllegalArgumentException if the <var>command</var> is <code>null</code>
*/
- public Command parseCommand(String command) throws MulgaraParserException, MulgaraLexerException, Exception {
+ public Command parseCommand(String command) throws MulgaraParserException, MulgaraLexerException, IOException {
return parseQuery(command);
}
@@ -587,7 +587,7 @@
* @throws IOException if the <var>command</var> cannot be paersed
* @throws IllegalArgumentException if the <var>command</var> is <code>null</code>
*/
- public List<Command> parseCommands(String command) throws MulgaraParserException, MulgaraLexerException, Exception {
+ public List<Command> parseCommands(String command) throws MulgaraParserException, MulgaraLexerException, IOException {
throw new UnsupportedOperationException("Commands are not supported in this interface");
}
Added: branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlAutoInterpreter.java
===================================================================
--- branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlAutoInterpreter.java 2007-09-13 11:23:14 UTC (rev 433)
+++ branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlAutoInterpreter.java 2007-09-13 20:37:11 UTC (rev 434)
@@ -0,0 +1,116 @@
+/**
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.opensource.org/licenses/osl-3.0.txt
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ */
+package org.mulgara.itql;
+
+import java.net.URI;
+
+import org.apache.log4j.Logger;
+import org.mulgara.connection.Connection;
+import org.mulgara.parser.Interpreter;
+import org.mulgara.query.Answer;
+import org.mulgara.query.operation.Command;
+import org.mulgara.query.operation.LocalCommand;
+
+/**
+ * This class interprets TQL statements, and automatically executes them,
+ * establishing connections to servers when required.
+ *
+ * @created Sep 11, 2007
+ * @author Paul Gearon
+ * @copyright © 2007 <a href="mailto:gearon at ieee.org">Paul Gearon</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class TqlAutoInterpreter {
+ /** The logger. */
+ private final static Logger logger = Logger.getLogger(TqlAutoInterpreter.class.getName());
+
+ private Interpreter interpreter = new TqlInterpreter();
+
+ private String lastMessage;
+
+ private Answer lastAnswer;
+
+ private Exception lastException;
+
+ public TqlAutoInterpreter() {
+ resetState();
+ }
+
+
+ /**
+ * Execute a query. The results of the query will set the state of this object.
+ * @param command The string containing the query to execute.
+ * @return <code>false</code> if the command asks to exit, <code>true</code> to continue normal operation.
+ */
+ public boolean executeCommand(String command) {
+ resetState();
+
+ if (logger.isDebugEnabled()) logger.debug("Parsing the command: " + command);
+ Command cmd = null;
+ try {
+ cmd = interpreter.parseCommand(command);
+ } catch (Exception e) {
+ lastMessage = "Error parsing the query";
+ lastException = e;
+ }
+
+ // set up a connection, if required
+ Connection conn = null;
+ if (!cmd.isLocalOperation()) {
+ conn = establishConnection(cmd.getServerURI());
+ }
+
+ // execute the operation
+ try {
+ cmd.execute(conn);
+ lastMessage = cmd.getResultMessage();
+ } catch (Exception e) {
+ lastException = e;
+ lastMessage = "Error: " + e.getMessage();
+ }
+
+ // test if the command wants the user to quit
+ return cmd.isLocalOperation() && ((LocalCommand)cmd).isQuitCommand();
+ }
+
+
+ /** @return the message set from the last operation */
+ public String getLastMessage() { return lastMessage; }
+
+ /** @return the last answer returned from a query, or <code>null</code> if the last operation was not a query */
+ public Answer getLastAnswer() { return lastAnswer; }
+
+ /** @return the exception thrown from the last operation, or <code>null</code> if there was no error */
+ public Exception getLastException() { return lastException; }
+
+
+ /**
+ * Close any resources that are still in use.
+ */
+ public void close() {
+ }
+
+
+ /**
+ * Resets the internal state in preparation for a new operation to be executed.
+ */
+ private void resetState() {
+ lastMessage = null;
+ lastAnswer = null;
+ lastException = null;
+ }
+
+
+ private Connection establishConnection(URI serverUri) {
+ return null;
+ }
+}
Added: branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlCommandSplitter.java
===================================================================
--- branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlCommandSplitter.java 2007-09-13 11:23:14 UTC (rev 433)
+++ branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlCommandSplitter.java 2007-09-13 20:37:11 UTC (rev 434)
@@ -0,0 +1,100 @@
+/**
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.opensource.org/licenses/osl-3.0.txt
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ */
+package org.mulgara.itql;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Splits strings with multiple commands into lists of strings containing single commands.
+ * @created Sep 11, 2007
+ * @author Paul Gearon
+ * @copyright © 2007 <a href="mailto:gearon at ieee.org">Paul Gearon</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class TqlCommandSplitter implements CommandSplitter {
+ /** The logger. */
+ private final static Logger logger = Logger.getLogger(TqlCommandSplitter.class.getName());
+
+ /**
+ * Split a string into individual commands.
+ * @param commands A single string containing multiple commands.
+ * @return A list of strings each containing single commands.
+ */
+ public List<String> split(String commands) {
+ List<String> singleQueryList = new ArrayList<String>();
+
+ // Inside a URI?
+ boolean inUrl = false;
+ // Inside a text literal?
+ boolean inText = false;
+
+ // Start index for next single query
+ int startIndex = 0;
+
+ if (logger.isDebugEnabled()) logger.debug("About to break up query: " + commands);
+
+ commands = commands.trim();
+
+ // Iterate along the multi query and strip out the single queries.
+ for (int lineIndex = 0; lineIndex < commands.length(); lineIndex++) {
+
+ char currentChar = commands.charAt(lineIndex);
+
+ switch (currentChar) {
+
+ // Quote - end or start of a literal if not in a URI
+ // (OK so maybe it won't appear in a legal URI but let things further
+ // down handle this)
+ case '\'':
+ if (!inUrl) {
+ if (inText) {
+ // Check for an \' inside a literal
+ if ((lineIndex > 1) && (commands.charAt(lineIndex - 1) != '\\')) inText = false;
+ } else {
+ inText = true;
+ }
+ }
+ break;
+
+ // URI start - if not in a literal
+ case '<':
+ if (!inText) inUrl = true;
+ break;
+
+ // URI end - if not in a literal
+ case '>':
+ if (!inText) inUrl = false;
+ break;
+
+ case ';':
+ if (!inText && !inUrl) {
+ String singleQuery = commands.substring(startIndex, lineIndex + 1).trim();
+ startIndex = lineIndex + 1;
+ singleQueryList.add(singleQuery);
+ if (logger.isDebugEnabled()) logger.debug("Found single query: " + singleQuery);
+ }
+ break;
+
+ default:
+ }
+ }
+
+ // Lasy query is not terminated with a ';'
+ if (startIndex < commands.length()) singleQueryList.add(commands.substring(startIndex, commands.length()));
+
+ return singleQueryList;
+ }
+
+}
Modified: branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlInterpreter.java
===================================================================
--- branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlInterpreter.java 2007-09-13 11:23:14 UTC (rev 433)
+++ branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlInterpreter.java 2007-09-13 20:37:11 UTC (rev 434)
@@ -114,6 +114,14 @@
//
/**
+ * Creates a new ITQL command interpreter, using the default alias set.
+ */
+ public TqlInterpreter() {
+ this(getDefaultAliases());
+ }
+
+
+ /**
* Creates a new ITQL command interpreter.
*
* @param aliasMap the map from targets to aliases, never <code>null</code>
@@ -169,7 +177,7 @@
* @throws IOException if the <var>command</var> cannot be parsed
* @throws IllegalArgumentException if the <var>command</var> is <code>null</code>
*/
- public Command parseCommand(String command) throws MulgaraParserException, LexerException, Exception {
+ public Command parseCommand(String command) throws MulgaraParserException, MulgaraLexerException, IOException {
// validate command parameter
if ((command == null) || command.equals("")) {
@@ -186,7 +194,11 @@
// log the command abd push it into the lexer
this.logItql(command);
- lexer.add(command);
+ try {
+ lexer.add(command);
+ } catch (LexerException le) {
+ throw new MulgaraLexerException(le.getMessage(), le);
+ }
// test that this is a single command
if (lexer.getCommandCount() > 1) logger.warn("Multiple commands given to parser");
@@ -206,6 +218,10 @@
if (logger.isDebugEnabled()) logger.debug("Successfully parsed command " + command);
}
+ } catch (LexerException le) {
+ throw new MulgaraLexerException(le);
+ } catch (ParserException pe) {
+ throw new MulgaraParserException(pe);
} finally {
flush();
}
@@ -224,7 +240,7 @@
* @throws IOException if the <var>command</var> cannot be paersed
* @throws IllegalArgumentException if the <var>command</var> is <code>null</code>
*/
- public List<Command> parseCommands(String command) throws MulgaraParserException, LexerException, Exception {
+ public List<Command> parseCommands(String command) throws MulgaraParserException, MulgaraLexerException, IOException {
// validate command parameter
if ((command == null) || command.equals("")) {
@@ -241,8 +257,12 @@
variableFactory.reset();
// push the command into the lexer
- lexer.add(command);
-
+ try {
+ lexer.add(command);
+ } catch (LexerException le) {
+ throw new MulgaraLexerException(le);
+ }
+
// create a list of AST versions of the command
List<Command> commandList = new LinkedList<Command>();
@@ -265,9 +285,12 @@
if (logger.isDebugEnabled()) logger.debug("Successfully parsed command " + command);
- } catch (Exception e) {
+ } catch (ParserException e) {
flush();
- throw e;
+ throw new MulgaraParserException(e);
+ } catch (LexerException e) {
+ flush();
+ throw new MulgaraLexerException(e);
} catch (Error e) {
flush();
throw e;
Added: branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlSession.java
===================================================================
--- branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlSession.java 2007-09-13 11:23:14 UTC (rev 433)
+++ branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlSession.java 2007-09-13 20:37:11 UTC (rev 434)
@@ -0,0 +1,607 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.opensource.org/licenses/osl-3.0.txt
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ */
+
+package org.mulgara.itql;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+import java.util.ArrayList;
+import javax.swing.JFrame;
+import javax.swing.WindowConstants;
+import javax.xml.parsers.FactoryConfigurationError;
+
+// Third party packages
+import org.apache.log4j.BasicConfigurator;
+import org.apache.log4j.Category;
+import org.apache.log4j.xml.DOMConfigurator;
+
+import org.mulgara.query.Answer;
+import org.mulgara.query.TuplesException;
+
+
+/**
+ * Command line shell for working with TQL sessions.
+ *
+ * @created September 11, 2007
+ * @author Paul Gearon
+ * @copyright © 2007 <a href="mailto:pgearon at users.sourceforge.net">Paul Gearon</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class TqlSession {
+
+ private static final String USER_DIR = "user.dir";
+
+ private static final int DEFAULT_HEIGHT = 480;
+
+ private static final int DEFAULT_WIDTH = 640;
+
+ public static final String SHELL_NAME = "TQL Shell";
+
+ /** Line separator. */
+ private static final String EOL = System.getProperty("line.separator");
+
+ /** The prompt. */
+ public final static String PS1 = "iTQL> ";
+
+ /** The secondary prompt, indicating an incomplete command. */
+ public final static String PS2 = " ";
+
+ /** The log4j configuration file path (within the JAR file) */
+ private final static String LOG4J_CONFIG_PATH = "log4j-tql.xml";
+
+ /** The logging category to use */
+ private final static Category log = Category.getInstance(TqlSession.class.getName());
+
+ /** The default path to the pre-loading script */
+ private final static String PRE_LOADING_SCRIPT_PATH = "default-pre.tql";
+
+ /** The default path to the post-loading script */
+ private final static String POST_LOADING_SCRIPT_PATH = "default-post.tql";
+
+
+ /** The graphical UI. */
+ private TqlSessionUI gui;
+
+ /** The messages from the previous queries. */
+ private List<String> messages = new ArrayList<String>();
+
+ /** The answers from the previous queries. */
+ private List<Answer> answers = new ArrayList<Answer>();
+
+ /** The file name (URL) of the script to execute if -s is given. */
+ private String scriptLocation = null;
+
+ //
+ // Members
+ //
+
+ /** The TQL auto-interpreter associated with this session */
+ private final TqlAutoInterpreter autoTql;
+
+ /** The URL of the post-loading script */
+ private URL postLoadingScriptUrl = null;
+
+ /** The URL of the pre-loading script */
+ private URL preLoadingScriptUrl = null;
+
+ /** A functor for splitting commands apart. */
+ private CommandSplitter commandSplitter = null;
+
+
+ /**
+ * Start an interactive TQL session from the command prompt.
+ * @param args command line parameters
+ * @throws MalformedURLException Provided URL for a script file is invalid.
+ */
+ public static void main(String[] args) throws MalformedURLException {
+
+ // create a new session to work with
+ TqlSession tqlSession = new TqlSession();
+
+ try {
+
+ // set the default pre- and post-loading scripts
+ tqlSession.setDefaultLoadingScripts();
+
+ // parse the command line options
+ ItqlOptionParser optsParser = new ItqlOptionParser(args);
+ optsParser.parse();
+ boolean startSession = tqlSession.handleOptions(optsParser);
+
+ if (log.isDebugEnabled()) log.debug("Processed command line options");
+
+ // execute the pre-loading script - we need to do this after we get the
+ // command line options as we can override the defaults
+ tqlSession.executeLoadingScript(tqlSession.getPreLoadingScriptURL());
+
+ tqlSession.executeScript(tqlSession.getScript());
+
+ if (log.isDebugEnabled()) log.debug("Executed pre-loading script");
+
+ // if we can, execute this session using std in and std out
+ if (startSession) tqlSession.runInterface();
+ else tqlSession.executeLoadingScript(tqlSession.getPostLoadingScriptURL());
+
+ } catch (ItqlOptionParser.UnknownOptionException uoe) {
+ log.warn("Invalid command line option specified: " + uoe.getOptionName());
+ System.err.println("Invalid option: " + uoe.getOptionName());
+ tqlSession.printUsage(System.out);
+ } catch (ItqlOptionParser.IllegalOptionValueException iove) {
+ String optionMsg = "-" + iove.getOption().shortForm() + ", --" +
+ iove.getOption().longForm() + " = " + iove.getValue();
+ log.warn("Invalid command line option value specified: " + optionMsg);
+ System.err.println("Invalid option value: " + optionMsg);
+ tqlSession.printUsage(System.out);
+ }
+ tqlSession.close();
+ }
+
+
+ /**
+ * Constructor. Creates a new TQL session.
+ */
+ public TqlSession() {
+ // load the default logging configuration
+ this.loadLoggingConfig();
+ autoTql = new TqlAutoInterpreter();
+ commandSplitter = new TqlCommandSplitter();
+ }
+
+
+ /**
+ * Returns a list of messages (Strings) from the execution of the last
+ * command or series of commands. Successful and unsuccessful commands will
+ * have valid string objects.
+ * @return all the accumulated messages from the execution of the previous commands.
+ */
+ List<String> getLastMessages() {
+ return messages;
+ }
+
+
+ /**
+ * Returns a list of Answers from the execution of the last command or series
+ * of commands. Failures will not be included.
+ * @return all the accumulated Answers from the execution of the previous commands.
+ */
+ List<Answer> getLastAnswers() {
+ return answers;
+ }
+
+
+ /**
+ * Executes a series of commands the given command. Accumulates all the
+ * results of these commands into the answers and messages lists.
+ * @param command The command to execute
+ */
+ void executeCommand(String command) {
+ // Reset answers and messages
+ answers.clear();
+ messages.clear();
+
+ for (String query: commandSplitter.split(command)) {
+
+ if (log.isDebugEnabled()) log.debug("Starting execution of command \"" + command + "\"");
+
+ // execute the command
+ if (!autoTql.executeCommand(query)) {
+ close();
+ return;
+ }
+
+ if (log.isDebugEnabled()) log.debug("Completed execution of commmand \"" + command + "\"");
+
+ Exception e = autoTql.getLastException();
+ if (e != null) log.warn("Couldn't execute command", e);
+
+ // Add the message and answer
+ messages.add(autoTql.getLastMessage());
+
+ Answer answer = autoTql.getLastAnswer();
+ if (answer != null) answers.add(answer);
+ }
+ }
+
+
+ /**
+ * Executes a script. This is done separately to {@link org.mulgara.query.operation.ExecuteScript}
+ * as it expects to use a single established connection, while this method will establish new
+ * connections for each line, as appropriate.
+ * @param scriptURL the URL of the script to load. May be <code>null</code> in which
+ * case nothing will be done.
+ */
+ private void executeScript(URL scriptURL) {
+
+ if (scriptURL == null) return;
+
+ // log that we're executing the script
+ log.debug("Executing script from " + scriptURL);
+
+ // keep a record of the line number
+ int line = 0;
+
+ try {
+
+ // create a reader to read the contents of the script
+ BufferedReader scriptIn = new BufferedReader(new InputStreamReader(scriptURL.openStream()));
+
+ String command;
+ while ((command = scriptIn.readLine()) != null) {
+
+ line++;
+ command = command.trim();
+
+ if (!command.equals("")) {
+ autoTql.executeCommand(command);
+
+ Answer answer = autoTql.getLastAnswer();
+ if (answer != null) {
+ printAnswer(answer, System.out);
+ answer.close();
+ }
+
+ String lastMessage = autoTql.getLastMessage();
+ if ((lastMessage != null) && (lastMessage != "") && (gui != null)) System.out.println(lastMessage);
+ }
+
+ }
+ } catch (TuplesException te) {
+ System.err.println("Error accessing results (line " + line + "): " + te.getMessage());
+ log.warn("Unable to complete script - " + scriptURL + " (line " + line + ") - " + te);
+ } catch (IOException ioe) {
+ System.err.println("Could not execute script (line " + line + "): " + ioe);
+ log.warn("Unable to execute script - " + scriptURL + " (line " + line + ") - " + ioe);
+ }
+ }
+
+
+ /**
+ * Create a UI and start it up. Returns when the GUI has been exited.
+ */
+ private void runInterface() {
+ // Create the UI.
+ JFrame mainWindow = new JFrame(SHELL_NAME);
+ mainWindow.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
+ mainWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+
+ gui = new TqlSessionUI(this);
+ mainWindow.getContentPane().add(gui);
+
+ if (log.isInfoEnabled()) log.info("Starting TQL interpreter");
+
+ motdInitialization();
+
+ // Start the application, by making the UI visible
+ mainWindow.setVisible(true);
+
+ if (log.isInfoEnabled()) log.info("Stopping TQL interpreter");
+ if (log.isDebugEnabled()) log.debug("Executed post-loading script");
+ }
+
+
+ /**
+ * Initiates a session using the given <code>session</code>
+ * @param session the interactive session to issue commands to
+ * @param in the stream to read commands from
+ * @param out the stream to print responses to
+ */
+ private void motdInitialization() {
+ gui.print("TQL Command Line Interface" + EOL);
+ gui.print(EOL + "Type \"help ;\", then enter for help." + EOL + EOL + PS1);
+ }
+
+
+ /**
+ * Returns the location of the script to run.
+ * @return the location of the script to run.
+ * @throws MalformedURLException The script location is not a valid URL.
+ */
+ private URL getScript() throws MalformedURLException {
+ return new URL(scriptLocation);
+ }
+
+
+ /**
+ * @return the URL of the pre-loading script
+ */
+ private URL getPreLoadingScriptURL() {
+ return preLoadingScriptUrl;
+ }
+
+
+ /**
+ * @return the URL of the post-loading script
+ */
+ private URL getPostLoadingScriptURL() {
+ return postLoadingScriptUrl;
+ }
+
+
+ /**
+ * Closes the session associated with this interpreter, and ends the program.
+ * Subclasses that override this method <strong>must</strong> call
+ * <code>super.close()</code>.
+ */
+ private void close() {
+ // Close the session, if any
+ if (autoTql != null) autoTql.close();
+ System.exit(0);
+ }
+
+
+ /**
+ * Locates and sets the default loading scripts.
+ */
+ private void setDefaultLoadingScripts() {
+ preLoadingScriptUrl = locateScript(PRE_LOADING_SCRIPT_PATH);
+ postLoadingScriptUrl = locateScript(POST_LOADING_SCRIPT_PATH);
+ }
+
+
+ /**
+ * <p>Locates the loading script with the given path.</p>
+ * <p>This locates scripts in the following order:</p>
+ * <ol>
+ * <li> Current working directory;</li>
+ * <li> System classpath (if embedded in a JAR).</li>
+ * </ol>
+ * <p>Note. These could be overwritten by the command-line options <code>-o</code>
+ * and <code>-p</code>.</p>
+ *
+ * @param scriptPath the path to the script to locate
+ * @return a URL to the script, <code>null</code> if the script could not be found
+ */
+ private URL locateScript(String scriptPath) {
+
+ URL scriptUrl = null;
+
+ // find the current directory
+ String currentDirectory = System.getProperty(USER_DIR);
+
+ // append a "/" if we need to
+ if (!currentDirectory.endsWith("/")) currentDirectory += File.separator;
+
+ log.debug("Looking for script " + scriptPath + " in " + currentDirectory);
+
+ // try to find the script
+ File loadingScript = new File(currentDirectory + scriptPath);
+
+ if (loadingScript.exists() && loadingScript.isFile()) {
+
+ // found the URL. Return it.
+ log.debug("Found loading script - " + loadingScript);
+ try {
+ scriptUrl = loadingScript.toURL();
+ } catch (MalformedURLException mue) {
+ log.warn("Unable to convert loading script filename to URL - " + mue.getMessage());
+ System.err.println("Unable to convert loading script filename " + "to URL - " + loadingScript);
+ }
+
+ } else {
+ log.debug("Looking for loading script " + scriptPath + " in classpath");
+ // try to obtain the URL from the classpath
+ URL loadingScriptUrl = ClassLoader.getSystemResource(scriptPath);
+ if (loadingScriptUrl != null) {
+ log.debug("Found loading script at - " + loadingScriptUrl);
+ scriptUrl = loadingScriptUrl;
+ }
+ }
+
+ return scriptUrl;
+ }
+
+
+ /**
+ * Executes the pre-loading script.
+ * @param loadingScriptUrl the URL of the loading (pre/post) script to execute
+ */
+ private void executeLoadingScript(URL loadingScriptUrl) {
+ if (loadingScriptUrl != null) {
+ log.debug("Executing loading script " + loadingScriptUrl);
+ executeScript(loadingScriptUrl);
+ }
+ }
+
+
+ /**
+ * Processes the command line options passed to the interpreter.
+ * @param parser the command line option parser to use to parse the command line options
+ * @return <code>true</code> if the UI is required, <code>false</code> if the input is a script.
+ */
+ private boolean handleOptions(ItqlOptionParser parser) {
+
+ log.debug("Processing command line options");
+
+ try {
+ // find out if the user wants help
+ if (parser.getOptionValue(ItqlOptionParser.HELP) != null) {
+ printUsage(System.out);
+ return false; // don't start the UI
+ } else {
+
+ // dump the interpreter configuration
+ if (null != parser.getOptionValue(ItqlOptionParser.DUMP_CONFIG)) dumpConfig();
+
+ // load an external interpreter configuration file
+ String itqlConf = (String)parser.getOptionValue(ItqlOptionParser.ITQL_CONFIG);
+ if (itqlConf != null) loadItqlConfig(new URL(itqlConf));
+
+ // load an external logging configuration file
+ String logConf = (String)parser.getOptionValue(ItqlOptionParser.LOG_CONFIG);
+ if (logConf != null) loadLoggingConfig(new URL((String)logConf));
+
+ // find out whether to execute pre-and post loading scripts
+ if (null == parser.getOptionValue(ItqlOptionParser.NO_LOAD)) {
+
+ // override the default pre-loading script
+ String preScript = (String)parser.getOptionValue(ItqlOptionParser.PRE_SCRIPT);
+ if (preScript != null) preLoadingScriptUrl = new URL(preScript);
+
+ // override the default post-loading script
+ String postScript = (String)parser.getOptionValue(ItqlOptionParser.POST_SCRIPT);
+ if (postScript != null) postLoadingScriptUrl = new URL(preScript);
+
+ } else {
+
+ log.debug("Pre-loading and post-loading scripts disabled");
+ preLoadingScriptUrl = null;
+ postLoadingScriptUrl = null;
+ }
+
+ // If there is a script to run, then return false, else true for no script
+ return null == parser.getOptionValue(ItqlOptionParser.SCRIPT);
+ }
+ } catch (MalformedURLException e) {
+ log.warn("Invalid URL on command line - " + e.getMessage());
+ System.err.println("Invalid URL - " + e.getMessage());
+ printUsage(System.out);
+ } catch (Exception e) {
+ log.warn("Could not start interpreter - " + e.getMessage());
+ System.err.println("Error - " + e.getMessage());
+ }
+ // fall through from exception
+ return false;
+ }
+
+
+ /**
+ * Prints the usage instructions for the interpreter.
+ * @param out An output stream to print the instructions to.
+ */
+ private void printUsage(PrintStream out) {
+ // build the usage message
+ StringBuffer usage = new StringBuffer();
+ usage.append("Usage: java -jar <jarfile> ");
+
+ usage.append("[-h|-n] ");
+ usage.append("[-l <url>] ");
+ usage.append("[-o <url>] ");
+ usage.append("[-p <url>] ");
+ usage.append("[-s <url>]");
+ usage.append(EOL).append(EOL);
+
+ usage.append("-h, --help display this help screen").append(EOL);
+ usage.append("-n, --noload do not execute pre- and post-loading ");
+ usage.append("scripts (useful with -s)").append(EOL);
+
+ usage.append("-l, --logconfig use an external logging configuration file").append(EOL);
+
+ usage.append("-o, --postload execute an iTQL script after interpreter stops,").append(EOL);
+ usage.append(" overriding default post-loading script").append(EOL);
+ usage.append("-p, --preload execute an iTQL script before interpreter starts,").append(EOL);
+ usage.append(" overriding default pre-loading script").append(EOL);
+ usage.append("-s, --script execute an iTQL script and quit").append(EOL);
+ usage.append(EOL);
+ usage.append("The intepreter executes default pre- and post-loading scripts. These can be").append(EOL);
+ usage.append("used to load aliases etc. into the interpreter to simplify commands. The").append(EOL);
+ usage.append("default scripts are contained within the JAR file, however you can overide").append(EOL);
+ usage.append("these by placing files named default-pre.itql and default-post.itql in").append(EOL);
+ usage.append("the directory from which you run the interpreter, or by using the -p and").append(EOL);
+ usage.append("-o options.").append(EOL);
+
+ // print the usage
+ out.println(usage.toString());
+ }
+
+
+ /**
+ * Dunps the current interpreter configuration to the current directory. This
+ * will dump the entire interpreter configuration including the logging and
+ * application logging.
+ */
+ private void dumpConfig() {
+ // we don't support this feature yet
+ throw new UnsupportedOperationException();
+ }
+
+
+ /**
+ * Loads an external TQL interpreter configuration file. This will use the
+ * configuration in the file located at <code>itqlConfURL</code>, instead of
+ * the configuration contained within the distribution JAR file.
+ *
+ * @param configUrl the URL of the external iTQL interpreter configuration file
+ * @return <code>true</code> for successful loading of the file.
+ */
+ private boolean loadItqlConfig(URL configUrl) {
+ // we don't support this feature yet
+ throw new UnsupportedOperationException();
+ }
+
+
+ /**
+ * Loads an external XML log4j configuration file. This will use the
+ * configuration in the file located at <code>logConfURL</code>, instead of
+ * the configuration contained within the distribution JAR file.
+ * @param logConfUrl the URL of the external XML log4j configuration file
+ * @throws Exception if unable to complete the method sucessfully
+ */
+ private void loadLoggingConfig(URL logConfUrl) throws Exception {
+ // configure the logging service
+ DOMConfigurator.configure(logConfUrl);
+ log.info("Using new logging configuration from " + logConfUrl);
+ }
+
+
+ /**
+ * Loads the embedded logging configuration (from the JAR file).
+ */
+ private void loadLoggingConfig() {
+ // get a URL from the classloader for the logging configuration
+ URL log4jConfigUrl = ClassLoader.getSystemResource(LOG4J_CONFIG_PATH);
+
+ // if we didn't get a URL, tell the user that something went wrong
+ if (log4jConfigUrl == null) {
+ System.err.println("Unable to find logging configuration file in JAR " +
+ "with " + LOG4J_CONFIG_PATH + ", reverting to default configuration.");
+ BasicConfigurator.configure();
+ } else {
+ try {
+ // configure the logging service
+ DOMConfigurator.configure(log4jConfigUrl);
+ log.info("Using logging configuration from " + log4jConfigUrl);
+ } catch (FactoryConfigurationError e) {
+ System.err.println("Unable to configure logging service");
+ }
+ }
+ }
+
+
+ /**
+ * Prints an answer to a print stream.
+ * @param answer The answer to print
+ * @param out The print stream to send the answer to.
+ * @throws TuplesException There was an error moving through the data in the answer.
+ */
+ private void printAnswer(Answer answer, PrintStream out) throws TuplesException {
+ answer.beforeFirst();
+ if (answer.isUnconstrained()) {
+ out.println("[ true ]");
+ } else {
+ while (answer.next()) {
+ out.print("[ ");
+ for (int index = 0; index < answer.getNumberOfVariables(); index++) {
+ out.print(String.valueOf(answer.getObject(index)));
+ if (index < (answer.getNumberOfVariables() - 1)) out.print(", ");
+ }
+ out.println(" ]");
+ }
+ }
+ }
+
+}
Added: branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlSessionUI.java
===================================================================
--- branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlSessionUI.java 2007-09-13 11:23:14 UTC (rev 433)
+++ branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlSessionUI.java 2007-09-13 20:37:11 UTC (rev 434)
@@ -0,0 +1,875 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is the Kowari Metadata Store.
+ *
+ * The Initial Developer of the Original Code is Plugged In Software Pty
+ * Ltd (http://www.pisoftware.com, mailto:info at pisoftware.com). Portions
+ * created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
+ * Plugged In Software Pty Ltd. All Rights Reserved.
+ *
+ * Contributor(s): N/A.
+ *
+ * [NOTE: The text of this Exhibit A may differ slightly from the text
+ * of the notices in the Source Code files of the Original Code. You
+ * should use the text of this Exhibit A rather than the text found in the
+ * Original Code Source Code for Your Modifications.]
+ */
+
+package org.mulgara.itql;
+
+/**
+ * Swing based iTQL session command line shell.
+ *
+ * @created 2004-01-15
+ *
+ * @author Andrew Newman
+ *
+ * @version $Revision: 1.8 $
+ *
+ * @modified $Date: 2005/01/05 04:58:15 $ by $Author: newmana $
+ *
+ * @company <A href="mailto:info at PIsoftware.com">Plugged In Software</A>
+ *
+ * @copyright ©2004 <a href="http://www.pisoftware.com/">Plugged In
+ * Software Pty Ltd</a>
+ *
+ * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
+ */
+import java.io.*;
+import java.util.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.text.*;
+
+import org.apache.log4j.*;
+import org.jrdf.graph.Node;
+
+import org.mulgara.query.Answer;
+
+ at SuppressWarnings("serial")
+public class TqlSessionUI extends JScrollPane implements Runnable,
+ KeyListener, java.awt.event.MouseListener, ActionListener {
+
+ /**
+ * The logging category to log to
+ */
+ private final static Logger log = Logger.getLogger(TqlSessionUI.class);
+
+ /**
+ * Inputstream to take user input.
+ */
+ private InputStream in;
+
+ /**
+ * Outputstream to display user output.
+ */
+ private PrintStream out;
+
+ /**
+ * Used to pipe input.
+ */
+ private InputStream inPipe;
+
+ /**
+ * Used to pipe output.
+ */
+ private OutputStream outPipe;
+
+ /**
+ * The iTQL session to send queries and used to send results.
+ */
+ private TqlSession tqlSession;
+
+ /**
+ * The list of history items.
+ */
+ private ArrayList history = new ArrayList();
+
+ /**
+ * Current index into the history.
+ */
+ private int historyIndex = 0;
+
+ /**
+ * Current cursor position.
+ */
+ private int cursorPosition = 0;
+
+ /**
+ * The UI widget for displaying all text.
+ */
+ private JTextPane text;
+
+ /**
+ * The default styled document.
+ */
+ private DefaultStyledDocument doc;
+
+ /**
+ * Popup menu for Windows users.
+ */
+ private JPopupMenu popupMenu = new JPopupMenu();
+
+ /**
+ * Whether we are running a command still.
+ */
+ private volatile boolean runningCommand = false;
+
+ /**
+ * Create a new UI representation.
+ *
+ * @param newItqlSession the itql session to call when we receive commands and
+ * when we want to display them.
+ */
+ public TqlSessionUI(TqlSession newTqlSession) {
+
+ super();
+
+ tqlSession = newTqlSession;
+ doc = new DefaultStyledDocument();
+ text = new PasteablePane(doc);
+ text.setFont(new Font("Monospaced", Font.PLAIN, 12));
+ text.setMargin(new Insets(5, 5, 5, 5));
+ text.addKeyListener(this);
+ text.addMouseListener(this);
+ setViewportView(text);
+
+ // Consume middle click to handle properly for Unix/Linux
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ toolkit.addAWTEventListener(new MouseListener(), AWTEvent.MOUSE_EVENT_MASK);
+
+ // Add popup menu for Windows users.
+ JMenuItem copyItem = new JMenuItem("Copy");
+ JMenuItem pasteItem = new JMenuItem("Paste");
+ popupMenu.add(copyItem);
+ popupMenu.add(pasteItem);
+ copyItem.addActionListener(this);
+ pasteItem.addActionListener(this);
+
+ outPipe = new PipedOutputStream();
+ try {
+
+ in = new PipedInputStream((PipedOutputStream) outPipe);
+ } catch (IOException e) {
+
+ log.error("Error creating input stream", e);
+ }
+
+ PipedOutputStream pout = new PipedOutputStream();
+ out = new PrintStream(pout);
+ try {
+
+ inPipe = new PipedInputStream(pout);
+ } catch (IOException e) {
+
+ log.error("Error creating input pipe", e);
+ }
+
+ // Start the inpipe watcher
+ new Thread(this).start();
+ requestFocus();
+ }
+
+ public void requestFocus() {
+
+ super.requestFocus();
+ text.requestFocus();
+ }
+
+ /**
+ * Handle key pressed event.
+ *
+ * @param e the key that was pressed.
+ */
+ public void keyPressed(KeyEvent e) {
+
+ switch (e.getKeyCode()) {
+
+ // Enter pressed
+ case (KeyEvent.VK_ENTER):
+
+ if (e.getID() == KeyEvent.KEY_PRESSED) {
+
+ if (!runningCommand) {
+
+ enterPressed();
+ cursorPosition = textLength();
+ text.setCaretPosition(cursorPosition);
+ }
+ }
+ e.consume();
+ text.repaint();
+ break;
+
+ // Up history
+ case (KeyEvent.VK_UP):
+
+ if (e.getID() == KeyEvent.KEY_PRESSED) {
+
+ historyUp();
+ }
+ e.consume();
+ break;
+
+ // Down history
+ case (KeyEvent.VK_DOWN):
+ if (e.getID() == KeyEvent.KEY_PRESSED) {
+
+ historyDown();
+ }
+ e.consume();
+ break;
+
+ // Left or delete.
+ case (KeyEvent.VK_LEFT):
+ case (KeyEvent.VK_DELETE):
+
+ if (text.getCaretPosition() <= cursorPosition) {
+
+ e.consume();
+ }
+ break;
+
+ // Go right.
+ case (KeyEvent.VK_RIGHT):
+
+ if (text.getCaretPosition() < cursorPosition) {
+
+ // move caret first!
+ }
+ text.repaint();
+ break;
+
+ // Control-A go to start of line.
+ case (KeyEvent.VK_A):
+
+ if ((e.getModifiers() & InputEvent.CTRL_MASK) > 0) {
+
+ text.setCaretPosition(cursorPosition);
+ e.consume();
+ }
+ break;
+
+ // Control-C copy the text.
+ case (KeyEvent.VK_C):
+
+ if (text.getSelectedText() == null) {
+
+ text.copy();
+ e.consume();
+ }
+ break;
+
+
+ // Control-E go to end of line.
+ case (KeyEvent.VK_E):
+
+ if ((e.getModifiers() & InputEvent.CTRL_MASK) > 0) {
+
+ text.setCaretPosition(textLength());
+ e.consume();
+ }
+ break;
+
+ // Control-U remove line
+ case (KeyEvent.VK_U):
+
+ if ((e.getModifiers() & InputEvent.CTRL_MASK) > 0) {
+
+ replaceText("", cursorPosition, textLength());
+ historyIndex = 0;
+ e.consume();
+ }
+ break;
+
+ // Home go to start of line
+ case (KeyEvent.VK_HOME):
+
+ text.setCaretPosition(cursorPosition);
+ e.consume();
+ break;
+
+ // Go to end of line
+ case (KeyEvent.VK_END):
+
+ text.setCaretPosition(textLength());
+ e.consume();
+ break;
+
+ // Ignore modifiers
+ case (KeyEvent.VK_ALT):
+ case (KeyEvent.VK_CAPS_LOCK):
+ case (KeyEvent.VK_CONTROL):
+ case (KeyEvent.VK_ESCAPE):
+ case (KeyEvent.VK_F1):
+ case (KeyEvent.VK_F2):
+ case (KeyEvent.VK_F3):
+ case (KeyEvent.VK_F4):
+ case (KeyEvent.VK_F5):
+ case (KeyEvent.VK_F6):
+ case (KeyEvent.VK_F7):
+ case (KeyEvent.VK_F8):
+ case (KeyEvent.VK_F9):
+ case (KeyEvent.VK_F10):
+ case (KeyEvent.VK_F11):
+ case (KeyEvent.VK_F12):
+ case (KeyEvent.VK_INSERT):
+ case (KeyEvent.VK_META):
+ case (KeyEvent.VK_PAUSE):
+ case (KeyEvent.VK_PRINTSCREEN):
+ case (KeyEvent.VK_SHIFT):
+ case (KeyEvent.VK_SCROLL_LOCK):
+
+ // Do nothing.
+
+ break;
+
+ // Handle normal characters
+ default:
+
+ if ( (e.getModifiers() & (InputEvent.ALT_MASK | InputEvent.CTRL_MASK |
+ InputEvent.META_MASK)) == 0) {
+
+ if (text.getCaretPosition() < cursorPosition) {
+
+ text.setCaretPosition(textLength());
+ }
+ text.repaint();
+ }
+
+ // Handle back space - don't let it go too far back.
+ if (e.paramString().indexOf("Backspace") != -1) {
+
+ if (text.getCaretPosition() <= cursorPosition) {
+
+ e.consume();
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ public void keyTyped(KeyEvent e) {
+
+ if (e.paramString().indexOf("Backspace") != -1) {
+
+ if (text.getCaretPosition() <= cursorPosition) {
+
+ e.consume();
+ }
+ }
+ }
+
+ public void keyReleased(KeyEvent e) {
+
+ // Do nothing.
+ }
+
+ public void mouseClicked(MouseEvent e) {
+
+ // Do nothing.
+ }
+
+ public void mouseEntered(MouseEvent e) {
+
+ // Do nothing.
+ }
+
+ public void mouseExited(MouseEvent e) {
+
+ // Do nothing.
+ }
+
+ public void mousePressed(MouseEvent e) {
+
+ if (e.isPopupTrigger()) {
+
+ popupMenu.show(e.getComponent(), e.getX(), e.getY());
+ }
+ }
+
+ public void mouseReleased(MouseEvent e) {
+
+ if (e.isPopupTrigger()) {
+
+ popupMenu.show(e.getComponent(), e.getX(), e.getY());
+ }
+ }
+
+ public void actionPerformed(ActionEvent event) {
+
+ String eventOccurred = event.getActionCommand();
+
+ if (eventOccurred.equals("Copy")) {
+
+ text.copy();
+ } else if (eventOccurred.equals("Paste")) {
+
+ text.paste();
+ }
+ }
+
+ /**
+ * Returns the length of the current text buffer.
+ *
+ * @return length of the current text buffer.
+ */
+ private int textLength() {
+
+ return text.getDocument().getLength();
+ }
+
+ /**
+ * Replaces the given string to a position in the currently displayed line.
+ *
+ * @param newString the string to add.
+ * @param start the starting position.
+ * @param end the end position.
+ */
+ private void replaceText(String newString, int start, int end) {
+
+ text.select(start, end);
+ text.replaceSelection(newString);
+ }
+
+ /**
+ * When the enter key has been pressed process the current command.
+ */
+ private void enterPressed() {
+
+ String command = getCommand();
+
+ // Create null command.
+ if (command.length() != 0) {
+
+ // Put the command at the end of the array.
+ history.add(command);
+ command = command + System.getProperty("line.separator");
+
+ // If the array gets too large remove the last entry.
+ if (history.size() > 30) {
+
+ history.remove(0);
+ }
+
+ // Indicate that we are running a command.
+ runningCommand = true;
+
+ // Create a new thread and start it.
+ ExecutionThread execThread = new ExecutionThread("execThread", command);
+ execThread.start();
+ } else {
+
+ // We've just hit enter so print the prompt.
+ printPrompt();
+ }
+ }
+
+ /**
+ * Prints out the prompt.
+ */
+ public void printPrompt() {
+
+ println();
+ print(ItqlSession.PROMPT);
+ historyIndex = 0;
+ text.repaint();
+ }
+
+ /**
+ * Returns the current command.
+ *
+ * @return the current command.
+ */
+ private String getCommand() {
+
+ String command = "";
+ try {
+
+ command = text.getText(cursorPosition, textLength() - cursorPosition);
+ } catch (BadLocationException e) {
+
+ log.error("Failed to get text command at position: " + cursorPosition,
+ e);
+ }
+
+ return command;
+ }
+
+ /**
+ * Display the next command in the history buffer.
+ */
+ private void historyUp() {
+
+ // Ensure there's a history and that the index never goes above the array
+ // size.
+ if ((history.size() != 0) && (historyIndex != history.size())) {
+
+ historyIndex++;
+ displayHistoryLine();
+ }
+ }
+
+ /**
+ * Display the previous command in the history buffer.
+ */
+ private void historyDown() {
+
+ // Ensure there's a history and that the index is initially above 1.
+ if ((history.size() != 0) && (historyIndex > 1)) {
+
+ historyIndex--;
+ displayHistoryLine();
+ }
+ }
+
+ /**
+ * Displays the history line to the screen.
+ */
+ private void displayHistoryLine() {
+
+ String showline = (String) history.get(history.size() - historyIndex);
+ replaceText(showline, cursorPosition, textLength());
+ text.setCaretPosition(textLength());
+ text.repaint();
+ }
+
+ /**
+ * Prints a message to the UI with a line separator.
+ *
+ * @param message the message to display.
+ */
+ public void println(String message) {
+
+ print(message + System.getProperty("line.separator"));
+ text.repaint();
+ }
+
+ /**
+ * Prints empty line.
+ */
+ public void println() {
+
+ print(System.getProperty("line.separator"));
+ text.repaint();
+ }
+
+ /**
+ * Prints a message to the UI.
+ *
+ * @param message the message to display.
+ */
+ public void print(final String message) {
+
+ invokeAndWait(new Runnable() {
+
+ public void run() {
+
+
+ append(message);
+ cursorPosition = textLength();
+ text.setCaretPosition(cursorPosition);
+ }
+ });
+ }
+
+ /**
+ * Print out an error message to the UI.
+ *
+ * @param errorMessage the error message to display.
+ */
+ public void error(String errorMessage) {
+
+ print(errorMessage, Color.red);
+ }
+
+ /**
+ * Print out the message with the given color using the current font.
+ *
+ * @param message the message to display.
+ * @param color the color to use.
+ */
+ public void print(String message, Color color) {
+
+ print(message, null, color);
+ }
+
+ /**
+ * Print out the message with the given font and colour. Uses invoke and
+ * wait.
+ *
+ * @param message the message to display.
+ * @param font the font to use.
+ * @param color the color to use.
+ */
+ public void print(final String message, final Font font, final Color color) {
+
+ invokeAndWait(new Runnable() {
+
+ public void run() {
+
+ try {
+
+ AttributeSet oldStyle = text.getCharacterAttributes();
+ setStyle(font, color);
+ append(message);
+ cursorPosition = textLength();
+ text.setCaretPosition(cursorPosition);
+ text.setCharacterAttributes(oldStyle, true);
+ } catch (Exception e) {
+
+ log.error("Error when printing: " + message, e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Sets the new style of a font and color to the text.
+ *
+ * @param font the new font.
+ * @param color the new color.
+ * @return the attributes of the given font and color.
+ */
+ private AttributeSet setStyle(Font font, Color color) {
+
+ MutableAttributeSet attr = new SimpleAttributeSet();
+ StyleConstants.setForeground(attr, color);
+
+ // Don't set if null
+ if (font != null) {
+
+ if (font.isBold()) {
+
+ StyleConstants.setBold(attr, true);
+ } else {
+
+ StyleConstants.setBold(attr, false);
+ }
+
+ StyleConstants.setFontFamily(attr, font.getFamily());
+ StyleConstants.setFontSize(attr, font.getSize());
+ }
+ text.setCharacterAttributes(attr, false);
+ return text.getCharacterAttributes();
+ }
+
+ /**
+ * Append the given string to the existing string.
+ *
+ * @param newString the string to append to.
+ */
+ private void append(String newString) {
+
+ int length = textLength();
+ text.select(length, length);
+ text.replaceSelection(newString);
+ }
+
+ /**
+ * Thread that runs while waiting for input.
+ */
+ public void run() {
+
+ try {
+
+ byte[] buffer = new byte[255];
+ int read;
+ while ((read = inPipe.read(buffer)) != -1) {
+
+ print(new String(buffer, 0, read));
+ }
+ } catch (IOException e) {
+
+ log.error("Error reading input", e);
+ }
+ }
+
+ /**
+ * If not in the event thread run via SwingUtilities.invokeAndWait()
+ */
+ private void invokeAndWait(Runnable runnable) {
+
+ if (!SwingUtilities.isEventDispatchThread()) {
+
+ try {
+
+ SwingUtilities.invokeAndWait(runnable);
+ } catch (Exception e) {
+
+ log.error("Error while executing invoke and wait", e);
+ }
+ } else {
+
+ runnable.run();
+ }
+ }
+
+ /**
+ * Extension to JTextPane to put all pastes at the end of the command line.
+ */
+ class PasteablePane extends JTextPane {
+
+ public PasteablePane(StyledDocument doc) {
+ super(doc);
+ }
+
+ public void paste() {
+ super.paste();
+ }
+ }
+
+ class MouseListener implements AWTEventListener {
+
+ public void eventDispatched(AWTEvent event) {
+
+ MouseEvent me = (MouseEvent) event;
+ if (me.getButton() == MouseEvent.BUTTON2) {
+
+ me.consume();
+ }
+ }
+ }
+
+ /**
+ * Executes the command in a separate thread and display the results.
+ */
+ class ExecutionThread extends Thread {
+
+ /**
+ * The command to execute.
+ */
+ private String command;
+
+ /**
+ * Create a new execution thread.
+ *
+ * @param threadName the name of the thread.
+ * @param newCommand the iTQL command to execute.
+ */
+ public ExecutionThread(String threadName, String newCommand) {
+
+ super(threadName);
+ command = newCommand;
+ }
+
+ /**
+ * Run the command and display answer results.
+ */
+ public void run() {
+
+ tqlSession.executeCommand(command);
+ println();
+
+ java.util.List answers = tqlSession.getLastAnswers();
+ java.util.List messages = tqlSession.getLastMessages();
+
+ int answerIndex = 0;
+ String lastMessage;
+
+ while (answerIndex < answers.size()) {
+
+ lastMessage = (String) messages.get(answerIndex);
+
+ try {
+
+ // Assume the same number of answers and messages
+ Answer answer = (Answer) answers.get(answerIndex);
+
+ // If there's more than one answer print a heading.
+ if (answers.size() > 1) {
+
+ println();
+ // If there's more than one answer add an extra line before the
+ // heading.
+ print("Executing Query " + (answerIndex+1),
+ new Font("Monospaced", Font.BOLD, 12), Color.BLACK);
+ println();
+ }
+
+ // print the results
+ if (answer != null) {
+
+ boolean hasAnswers = true;
+
+ answer.beforeFirst();
+ long rowCount = 0;
+
+ if (answer.isUnconstrained()) {
+ println("[ true ]");
+ rowCount = 1;
+ } else {
+ if (!answer.next()) {
+ print("No results returned.",
+ new Font("Monospaced", Font.BOLD, 12), Color.BLACK);
+ hasAnswers = false;
+ } else {
+ do {
+ rowCount++;
+ print("[ ");
+ for (int index = 0; index < answer.getNumberOfVariables();
+ index++) {
+ Object object = answer.getObject(index);
+
+ assert(object instanceof Answer) ||
+ (object instanceof Node ) ||
+ (object == null);
+
+ print(String.valueOf(object));
+ if (index < (answer.getNumberOfVariables() - 1)) {
+ print(", ");
+ }
+ }
+ println(" ]");
+ }
+ while (answer.next());
+ }
+ }
+ if (hasAnswers) {
+ print(rowCount + " rows returned.",
+ new Font("Monospaced", Font.BOLD, 12), Color.BLACK);
+ }
+
+ answer.close();
+ }
+ } catch (Exception te) {
+
+ // Failed to iterate over or retrieve the answer.
+ log.fatal("Failed to retrieve or iterate over answer", te);
+ error("Failed to get answer");
+ }
+
+ if ((lastMessage != null) && (lastMessage != "")) {
+
+ print(lastMessage, new Font("Monospaced", Font.BOLD, 12),
+ Color.BLACK);
+ }
+
+ // If there's more than one answer add a new line.
+ if (answers.size() > 1) {
+
+ println();
+ }
+
+ // Increment index
+ answerIndex++;
+ }
+
+ // Signal that the command has finished and display prompt
+ runningCommand = false;
+ printPrompt();
+ }
+ };
+}
More information about the Mulgara-svn
mailing list