[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 &copy; 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 &copy; 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 &copy; 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 &copy; 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 &copy;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