[Mulgara-svn] r1128 - in trunk/src/jar: . webquery webquery/java webquery/java/org webquery/java/org/mulgara webquery/java/org/mulgara/webquery webquery/java/org/mulgara/webquery/html webquery/resources webquery/resources/images webquery/resources/styles

pag at mulgara.org pag at mulgara.org
Sun Aug 10 02:06:09 UTC 2008


Author: pag
Date: 2008-08-09 19:06:08 -0700 (Sat, 09 Aug 2008)
New Revision: 1128

Added:
   trunk/src/jar/webquery/
   trunk/src/jar/webquery/build.xml
   trunk/src/jar/webquery/java/
   trunk/src/jar/webquery/java/org/
   trunk/src/jar/webquery/java/org/mulgara/
   trunk/src/jar/webquery/java/org/mulgara/webquery/
   trunk/src/jar/webquery/java/org/mulgara/webquery/QueryParams.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/QueryResponsePage.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/QueryServlet.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/RequestException.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/ResourceBinaryFile.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/ResourceFile.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/ResourceTextFile.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/Template.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/Anchor.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/Break.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/Div.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/Emphasis.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/HtmlElement.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/Paragraph.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/Span.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/Strong.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/Table.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableAbstr.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableBody.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableData.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableHeader.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableRow.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableStructure.java
   trunk/src/jar/webquery/java/org/mulgara/webquery/html/Text.java
   trunk/src/jar/webquery/resources/
   trunk/src/jar/webquery/resources/debug.html
   trunk/src/jar/webquery/resources/images/
   trunk/src/jar/webquery/resources/images/logo.jpg
   trunk/src/jar/webquery/resources/styles/
   trunk/src/jar/webquery/resources/styles/ui.css
   trunk/src/jar/webquery/resources/template.html
   trunk/src/jar/webquery/resources/template_head.html
   trunk/src/jar/webquery/resources/template_more_tail.html
   trunk/src/jar/webquery/resources/template_tail.html
   trunk/src/jar/webquery/webquery-build.properties
Log:
A new query UI provided as a web service. This is simpler than the webui WAR file, and has no dependencies on XMLC or Barracuda

Added: trunk/src/jar/webquery/build.xml
===================================================================
--- trunk/src/jar/webquery/build.xml	                        (rev 0)
+++ trunk/src/jar/webquery/build.xml	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE project>
+
+<!-- =================================================================== -->
+<!--                       Project definition                            -->
+<!-- =================================================================== -->
+<project name="webquery" default="webquery-jar" basedir="../../..">
+
+  <!-- =================================================================== -->
+  <!--                      Property Definitions                           -->
+  <!-- =================================================================== -->
+
+  <!-- =================================================================== -->
+  <!--                              Imports                                -->
+  <!-- =================================================================== -->
+
+  <!-- =================================================================== -->
+  <!--                          Path Definitions                           -->
+  <!-- =================================================================== -->
+  <path id="webquery-classpath">
+    <path refid="common-classpath"/>
+    <fileset file="${util.dist.dir}/${util.jar}"/>
+    <fileset file="${query.dist.dir}/${query.jar}"/>
+    <fileset file="${itql.dist.dir}/${itql.jar}"/>
+    <fileset file="${server.dist.dir}/${server.jar}"/>
+    <fileset file="${lib.dir}/${jetty.jar}"/>
+    <fileset file="${lib.dir}/${jetty-plus.jar}"/>
+    <fileset file="${lib.dir}/${servlet.jar}"/>
+  </path>
+
+  <path id="webquery-test-classpath">
+    <path refid="webquery-classpath"/>
+    <fileset file="${webquery.dist.dir}/${webquery.jar}"/>
+  </path>
+
+  <target name="webquery-clean" description="Removes all compile generated files for the webquery">
+    <tstamp/>
+    <delete dir="${webquery.obj.dir}"/>
+    <delete dir="${webquery.test.dir}"/>
+    <delete dir="${webquery.dist.dir}"/>
+  </target>
+
+  <target name="-webquery-prepare"
+          description="Creates all directories associated with the webquery's compilation"
+          depends="-prepare-build">
+    <mkdir dir="${webquery.obj.dir}"/>
+    <mkdir dir="${webquery.test.dir}"/>
+    <mkdir dir="${webquery.dist.dir}"/>
+    <mkdir dir="${webquery.obj.dir}/classes"/>
+    <mkdir dir="${webquery.obj.dir}/classes/conf"/>
+    <mkdir dir="${webquery.obj.dir}/java"/>
+  </target>
+
+  <target name="-webquery-unjar-config"
+          depends="-webquery-unjar-config-uptodate"
+          unless="webquery.unjar.config.uptodate">
+    <unjar src="${bin.dir}/${config.jar}" dest="${webquery.obj.dir}/classes"/>
+  </target>
+
+  <target name="-webquery-unjar-config-uptodate">
+    <available file="${webquery.obj.dir}/classes/org/mulgara/config/MulgaraConfig.class"
+               property="webquery.unjar.config.uptodate"/>
+  </target>
+
+  <target name="webquery-compile"
+          depends="-webquery-prepare, util-jar, config-jar, itql-jar, query-jar, -webquery-unjar-config"
+          description="Compiles all webquery related files included generated
+                       source code"
+          unless="webquery.classes.uptodate">
+
+    <javac destdir="${webquery.obj.dir}/classes" debug="on" deprecation="on" source="1.5">
+      <classpath refid="webquery-classpath"/>
+      <src path="${webquery.src.dir}/java"/>
+    </javac>
+  </target>
+
+  <target name="webquery-jar"
+          depends="webquery-compile, config-database, -webquery-jar-uptodate"
+          unless="webquery.jar.uptodate"
+          description="Creates the webquery's jar/s">
+
+    <jar jarfile="${webquery.dist.dir}/${webquery.jar}">
+      <fileset dir="${webquery.obj.dir}/classes"/>
+      <fileset dir="${webquery.src.dir}" includes="resources/**/*"/>
+    </jar>
+  </target>
+
+  <target name="-webquery-jar-uptodate">
+    <uptodate property="webquery.jar.uptodate"
+              targetfile="${webquery.dist.dir}/${webquery.jar}">
+      <srcfiles dir="${webquery.obj.dir}" includes="**/*"/>
+      <srcfiles dir="${webquery.src.dir}" includes="resources/**/*"/>
+    </uptodate>
+  </target>
+
+  <target name="webquery-dist"
+          depends="webquery-jar"
+          unless="webquery.uptodate"
+          description="Performs all tasks related to finalising this webquery
+                       and readying it for distribution">
+
+    <!-- All that's involved in the final version of the webquery library
+         is the jar so we don't need to do anything because the dependencies
+         take care of the work. -->
+  </target>
+
+  <target name="webquery-test" depends="webquery-jar"
+          description="Carries out unit testing for this webquery">
+
+    <antcall target="component-test">
+      <param name="classpath.id" value="webquery-test-classpath"/>
+      <param name="dir" value="${webquery.src.dir}"/>
+      <param name="jar" value="${webquery.jar}"/>
+    </antcall>
+  </target>
+
+  <target name="webquery-javadoc" depends="webquery-jar"
+          description="Creates the javadoc for this webquery">
+    <antcall target="javadoc">
+      <param name="javadoc.package" value="org.mulgara.webquery.*"/>
+      <param name="javadoc.classpath" value="webquery-classpath"/>
+      <param name="javadoc.sourcepath" value="${webquery.src.dir}/java"/>
+    </antcall>
+  </target>
+
+  <target name="webquery-help" description="Displays the help information for this webquery">
+    <echo message="Welcome to the build script for ${name}."/>
+    <echo message=""/>
+    <echo message="These targets can be invoked as follows:"/>
+    <echo message="  ./build.sh &lt;target&gt;"/>
+    <echo message=""/>
+    <java fork="false" classname="org.apache.tools.ant.Main" newenvironment="false">
+      <jvmarg value="${arch.bits}"/>
+      <arg line="-buildfile ${webquery.src.dir}/build.xml -projecthelp"/>
+    </java>
+  </target>
+</project>

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/QueryParams.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/QueryParams.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/QueryParams.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,130 @@
+/*
+ * 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.webquery;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.mulgara.util.Pair;
+
+/**
+ * Represents the parameters used in an HTTP request.
+ *
+ * @created Aug 5, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class QueryParams {
+
+  /** The encoding to use for a URL */
+  static final String ENCODING = "UTF-8";
+
+  /** The parameter names and values. */
+  private Map<String,String> params = new LinkedHashMap<String,String>();
+
+  /**
+   * Create an empty set of parameters.
+   */
+  public QueryParams() {
+  }
+
+  /**
+   * Create a set of parameters from a single name/value pair.
+   * @param name The name of the parameter.
+   * @param value The value for the parameter.
+   */
+  public QueryParams(String name, Object value) {
+    params.put(name, value.toString());
+  }
+
+  /**
+   * Create a set of parameters from an array of name/value pairs.
+   * @param pairs An array of name/value pairs.
+   */
+  public QueryParams(Pair<String,String>... pairs) {
+    for (Pair<String,String> p: pairs) params.put(p.first(), p.second());
+  }
+
+  /**
+   * Create a set of parameters from a collection of name/value pairs.
+   * @param pairs A collection of name/value pairs.
+   */
+  public QueryParams(Collection<Pair<String,String>> pairs) {
+    for (Pair<String,String> p: pairs) params.put(p.first(), p.second());
+  }
+
+  /**
+   * Adds a name/value to the parameters.
+   * @param pair The name/value pair to be added.
+   * @return This parameters object.
+   */
+  public QueryParams add(Pair<String,String> pair) {
+    params.put(pair.first(), pair.second());
+    return this;
+  }
+
+  /**
+   * Adds a name/value to the parameters.
+   * @param name The name of the parameter to be added.
+   * @param value The valueof the parameter to be added.
+   * @return This parameters object.
+   */
+  public QueryParams add(String name, String value) {
+    params.put(name, value);
+    return this;
+  }
+
+  /**
+   * Adds an array of name/values to the parameters.
+   * @param pairs The name/value array to be added.
+   * @return This parameters object.
+   */
+  public QueryParams addAll(Pair<String,String>... pairs) {
+    for (Pair<String,String> p: pairs) params.put(p.first(), p.second());
+    return this;
+  }
+
+  /**
+   * Adds a collection of name/values to the parameters.
+   * @param pairs The name/value collection to be added.
+   * @return This parameters object.
+   */
+  public QueryParams addAll(Collection<Pair<String,String>> pairs) {
+    for (Pair<String,String> p: pairs) params.put(p.first(), p.second());
+    return this;
+  }
+
+  /**
+   * Converts this set of parameters to the query portion of a URL.
+   * @return a query to be added to a URL.
+   */
+  public String toString() {
+    try {
+      boolean first = true;
+      StringBuilder b = new StringBuilder();
+      for (Map.Entry<String,String> e: params.entrySet()) {
+        if (first) first = false;
+        else b.append("&amp;");
+        b.append(URLEncoder.encode(e.getKey(), ENCODING));
+        b.append("=").append(URLEncoder.encode(e.getValue(), ENCODING));
+      }
+      return b.toString();
+    } catch (UnsupportedEncodingException e) {
+      throw new IllegalArgumentException("Unable to encode with " + ENCODING + ": " + e.getMessage());
+    }
+  }
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/QueryResponsePage.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/QueryResponsePage.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/QueryResponsePage.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,483 @@
+/*
+ * 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.webquery;
+
+import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.jrdf.graph.BlankNode;
+import org.jrdf.graph.Literal;
+import org.jrdf.graph.Node;
+import org.jrdf.graph.URIReference;
+import org.mulgara.query.Answer;
+import org.mulgara.query.TuplesException;
+import org.mulgara.query.Variable;
+import org.mulgara.query.operation.Command;
+import org.mulgara.util.Pair;
+import org.mulgara.webquery.html.Anchor;
+import org.mulgara.webquery.html.HtmlElement;
+import org.mulgara.webquery.html.Span;
+import org.mulgara.webquery.html.Strong;
+import org.mulgara.webquery.html.Table;
+import org.mulgara.webquery.html.TableData;
+import org.mulgara.webquery.html.TableHeader;
+import org.mulgara.webquery.html.TableRow;
+import org.mulgara.webquery.html.Text;
+import org.mulgara.webquery.html.HtmlElement.Attr;
+import org.mulgara.webquery.html.HtmlElement.Entity;
+
+import static org.mulgara.webquery.Template.*;
+
+/**
+ * Constructs and emits the response page for a set of queries.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class QueryResponsePage {
+
+  /** The name of the system property that can override the page size. */
+  public static final String PAGE_SIZE_PROP = "webui.page.size";
+
+  /** The default number of results per page or per cell. */
+  public static final int DEFAULT_RESULTS_PER_PAGE = 250;
+
+  /** The number of results per page or per cell. */
+  static int resultsPerPage;
+
+    /** The request that asked for this page. */
+  HttpServletRequest request;
+
+  /** The structure for sending the page back to the client. */
+  HttpServletResponse response;
+
+  /** A map of tags to the values that should replace them. */
+  Map<String,String> tagMap;
+
+  /** The object for accepting the output stream. */
+  PrintWriter out = null;
+
+  /** The set of unfinished results to render, mapped to the remaining sizes. */
+  Map<Answer,Pair<Long,Command>> unfinishedResults;
+
+
+  static {
+    try {
+      resultsPerPage = Integer.parseInt(System.getProperty(PAGE_SIZE_PROP));
+    } catch (Exception e) {
+      resultsPerPage = DEFAULT_RESULTS_PER_PAGE;
+    }
+  }
+
+
+  /**
+   * Construct this page for responsing to a particular request environment.
+   * @param reqThe request that asked for this page.
+   * @param resp The structure for sending the page back to the client.
+   * @param tagMap A map of tags to the values that should replace them
+   */
+  @SuppressWarnings("unchecked")
+  public QueryResponsePage(HttpServletRequest req, HttpServletResponse resp, Map<String,String> tagMap) {
+    this.request = req;
+    this.response = resp;
+    this.tagMap = tagMap;
+    this.unfinishedResults = (Map<Answer,Pair<Long,Command>>)req.getSession().getAttribute(UNFINISHED_RESULTS);
+  }
+
+  
+  /**
+   * Send a result page to the client without any stats. This starts with a form template, and then
+   * follows up with a single result.
+   * @param cmd The command that was run.
+   * @param result The result returned from running the command.
+   * @throws IOException There was an error writing to the client, or reading resources.
+   */
+  public void writeResult(Command cmd, Answer result) throws IOException {
+    List<Command> commands = Collections.singletonList(cmd);
+    List<Object> results = Collections.singletonList((Object)result);
+    writeResults(-1L, commands, results);
+  }
+
+
+  /**
+   * Send a result page to the client. This starts with a form template, and then
+   * follows up with the results.
+   * @param time The time take to execute the commands to get these results.
+   * @param cmds The commands that were run.
+   * @param results The result list returned from running each command.
+   * @throws IOException There was an error writing to the client, or reading resources.
+   */
+  public void writeResults(long time, List<Command> cmds, List<Object> results) throws IOException {
+    response.setContentType("text/html");
+    response.setHeader("pragma", "no-cache");
+    PrintWriter output = getOutput();
+    // write the head of the page
+    new ResourceTextFile(TEMPLATE_HEAD, tagMap).sendTo(output);
+
+    // summarise the results first
+    writeResultSummary(time, results.size());
+
+    // write the results
+    Iterator<Command> c = cmds.iterator();
+    Iterator<Object> r = results.iterator();
+    while (c.hasNext()) writeResult(c.next(), r.next());
+
+    // Check that we exhausted the results, like we were supposed to
+    if (r.hasNext()) response.sendError(SC_INTERNAL_SERVER_ERROR, "Internal error: results do not match queries.");
+
+    // write the tail of the page
+    new ResourceTextFile(TEMPLATE_TAIL).sendTo(output);
+
+    output.close();
+  }
+
+
+  /**
+   * Write a summary for the results of all the executed commands.
+   * @param out The stream to write the results to.
+   * @param time The time taken to execute the commands, in milliseconds.
+   * @param size The number of commands executed.
+   */
+  private void writeResultSummary(long time, int nrCommands) throws IOException {
+    // short circuit if we don't have a summary
+    if (time < 0) return;
+    HtmlElement row = new TableRow(ROW_INDENT);
+    row.add(new Span(CSS_LARGE, "Results:"));
+    Text t = new Text("(" + nrCommands);
+    t.append(nrCommands == 1 ? " query, " : " queries, ");
+    t.append(String.format("%.3f", (time / 1000.0))).append(" seconds)");
+    row.add(t);
+
+    emitTopLevelElement(row);
+  }
+
+
+  /**
+   * Write a single result into a row of an existing table.
+   * @param cmd The command executed to give this result.
+   * @param result The result for the command cmd.
+   * @throws IOException Occurs for an error writing the response.
+   */
+  private void writeResult(Command cmd, Object result) throws IOException {
+    if (result instanceof Answer) writeAnswerResult(cmd, (Answer)result);
+    else writeSimpleResult(cmd);
+  }
+
+
+  /**
+   * Write a simple result into a row of an existing table.
+   * @param cmd The command executed to give this result.
+   * @param result The result for the command cmd.
+   * @throws IOException Occurs for an error writing the response.
+   */
+  private void writeSimpleResult(Command cmd) throws IOException {
+    HtmlElement row = new TableRow(ROW_INDENT);
+    row.add(new Span(CSS_LARGE, "Query Executed:"));
+    row.add(new TableData(cmd.getText()).addAttr(Attr.VALIGN, "top").addAttr(Attr.WIDTH, "100%"));
+    
+    emitTopLevelElement(row);
+
+    row = new TableRow(ROW_INDENT);
+    row.add(new Span(CSS_LARGE, "Result Message:"));
+    row.add(new TableData(cmd.getResultMessage()).addAttr(Attr.VALIGN, "top").addAttr(Attr.WIDTH, "100%"));
+
+    emitTopLevelElement(row);
+  }
+
+
+  /**
+   * Write a complex result into a row of an existing table.
+   * @param cmd The command executed to give this result.
+   * @param result The result for the command cmd.
+   * @throws IOException Occurs for an error writing the response.
+   */
+  private void writeAnswerResult(Command cmd, Answer result) throws IOException {
+    // create a row with the query that was executed
+    TableRow row = new TableRow(ROW_INDENT);
+    row.add(new Span(CSS_LARGE, "Query Executed:"));
+    row.add(new TableData(cmd.getText()).addAttr(Attr.VALIGN, "top").addAttr(Attr.WIDTH, "100%"));
+
+    emitTopLevelElement(row);
+
+    // create the result table, and put it in a cell
+    TableData resultData = new TableData(createResultTable(result, cmd));
+    resultData.addAttr(Attr.COLSPAN, 2).addAttr(Attr.CLASS, CSS_RESULT_TABLE_CELL);
+    // create a new row to hold the result table
+    row = new TableRow(ROW_INDENT, resultData);
+
+    emitTopLevelElement(row);
+  }
+
+
+  /**
+   * Convert a result into an HTML table.
+   * @param result The result to convert.
+   * @return A new table containing all the requested results.
+   * @throws IOException If there was an error reading the results
+   */
+  private Table createResultTable(Answer result, Command cmd) throws IOException {
+    // create the table for the results
+    Table resultTable = new Table();
+    resultTable.addAttr(Attr.CLASS, CSS_RESULT_TABLE);
+
+    // Get the columns to be displayed
+    Variable[] vars = result.getVariables();
+
+    // add the headers for the table
+    TableRow headerRow = new TableRow();
+    for (Variable v: vars) headerRow.add(new TableHeader(new Strong(v.getName())));
+    resultTable.add(headerRow);
+
+    // add the result data
+    try {
+      int rowsLeft = resultsPerPage;
+      // if this is the first time we've seen this result, then go to the start
+      if (!isResuming(result)) result.beforeFirst();
+
+      while (result.next()) {
+        TableRow row = new TableRow();
+        for (int c = 0; c < vars.length; c++) row.add(createEmbeddableElement(result.getObject(c), cmd));
+        resultTable.add(row);
+        // exit early if this is too big
+        if (--rowsLeft == 0) {
+          addUnfinishedResult(result, resultsPerPage, cmd);
+          appendNextPageLink(result, resultTable);
+          return resultTable;
+        }
+      }
+      // got to the end of this result, so clean it up
+      resultFinished(result);
+    } catch (TuplesException e) {
+      throw new IOException("Error accessing the results of the query: " + e.getMessage());
+    }
+    return resultTable;
+  }
+
+
+  /**
+   * Converts an object returned from a query into an element in a table.
+   * If the object is a simple type, then it returns text linked to a query
+   * for that element. For Answer object, it returns a new table.
+   * @param obj The object to convert into a table element.
+   * @param cmd The command that gave rise to the object.
+   * @return A new table data element.
+   * @throws IOException If the data could not be read.
+   */
+  private TableData createEmbeddableElement(Object obj, Command cmd) throws IOException {
+    HtmlElement result = null;
+    if (obj instanceof Answer) result = createResultTable((Answer)obj, cmd);
+    if (obj instanceof BlankNode) result = new Text(obj.toString());
+    if (obj instanceof URIReference || obj instanceof Literal) result = getElementQuery((Node)obj);
+
+    // only null should make it this far without setting the result
+    if (result == null) {
+      if (obj != null) throw new IllegalArgumentException("Unknown type in result data");
+      result = new TableData(new Text(Entity.NBSP));
+    }
+
+    if (!(result instanceof TableData)) result = new TableData(result);
+    result.addAttr(Attr.CLASS, "rtd");  // set the style to Result-Table-Data
+    return (TableData)result;
+  }
+
+
+  /**
+   * Gets the URL used for the sample data.
+   * @return A URL for the sample data, expressed in the template as:
+   *         rmi://@@hostname@@/@@servername@@#sampledata
+   */
+  private Anchor getElementQuery(Node n) throws IOException {
+    try {
+      URI graphUri = new URI("rmi", tagMap.get(HOSTNAME_TAG), "/" + tagMap.get(SERVERNAME_TAG), "sampledata");
+      QueryParams params = new QueryParams();
+      params.add(MODEL_ARG, graphUri.toString());
+      String text;
+      if (n instanceof URIReference) {
+        params.add(QUERY_RESOURCE_ARG, n.toString());
+        text = n.toString();
+      } else {
+        params.add(QUERY_LITERAL_ARG, parameterizeLiteral((Literal)n));
+        text = ((Literal)n).getEscapedForm();
+      }
+      return new Anchor(new URI(EXECUTE_LINK + "?" + params), text);
+    } catch (URISyntaxException e) {
+      throw new IOException("Bad data returned from server");
+    }
+  }
+
+
+  /**
+   * Adds on a row without borders that links to the next page in this result.
+   * @param result The result being displayed.
+   * @param resultTable The existing page of data representing the result.
+   * @return The table with a new row appended to it, containing the required link.
+   * @throws IOException If there was an error structuring the table data.
+   */
+  private Table appendNextPageLink(Answer result, Table resultTable) throws IOException {
+    if (isResuming(result)) {
+      TableRow tr = createNextLinkRow(result, resultTable.getWidth());
+      tr.addAttr(Attr.CLASS, "borderLess");
+      resultTable.add(tr);
+    }
+    return resultTable;
+  }
+
+
+  /**
+   * Creates a link that will take the user to the next page for this data.
+   * @param result The result to create the link for.
+   * @param width The width the row has to fill.
+   * @return A table row containing the link to the next unfinished result.
+   * @throws IOException If there was an error structuring the table data.
+   */
+  private TableRow createNextLinkRow(Answer result, int width) throws IOException {
+    try {
+      QueryParams param = new QueryParams(RESULT_ORD_ARG, getNrToBeResumed(result));
+      Anchor a = new Anchor(new URI(EXECUTE_LINK + "?" + param), "Next page >");
+      a.addAttr(Attr.TITLE, "Forward to next page of results");
+      return new TableRow(new TableData(a).addAttr(Attr.COLSPAN, width));
+    } catch (URISyntaxException e) {
+      throw new IOException("Unabled to emit a relative URL: " + e.getMessage());
+    }
+  }
+
+
+  /**
+   * Writes a top-level element to the output stream.
+   * @param elt The element to write.
+   * @throws IOException If there was an error getting the stream or using it.
+   */
+  private void emitTopLevelElement(HtmlElement elt) throws IOException {
+    PrintWriter output = getOutput();
+    elt.sendTo(output);
+    output.append("\n");
+  }
+
+
+  /**
+   * Encode a Literal for use as a parameter in a query.
+   * @param l The literal to encode.
+   * @return An form of the literal that is safe to use in a parameter.
+   */
+  private String parameterizeLiteral(Literal l) {
+    StringBuilder p = new StringBuilder("'");
+    p.append(l.getLexicalForm().replaceAll("'", "\\\\'")).append('\'');
+
+    URI datatype = l.getDatatypeURI();
+    String language = l.getLanguage();
+
+    if (datatype != null) {
+      p.append("^^<").append(datatype).append(">");
+    } else if (language != null && !language.equals("")) {
+      p.append("@").append(language);
+    }
+
+    return p.toString();
+  }
+
+
+  /**
+   * Gets the PrintWriter for this page.
+   * @return The print writer to use for this page.
+   * @throws IOException If there was a problem getting the object.
+   */
+  private PrintWriter getOutput() throws IOException {
+    if (out == null) out = response.getWriter();
+    return out;
+  }
+
+
+  /**
+   * Determines how many results are to be resumed.
+   * @return <code>true</code> if this result has already been partly resumed.
+   */
+  private int getNrToBeResumed(Answer ans) {
+    if (unfinishedResults == null) throw new IllegalStateException("Should not be creating a link to a result when that result was not saved.");
+    Iterator<Answer> answers = unfinishedResults.keySet().iterator();
+    // should either be the first one added, or the last one
+    return (answers.next() == ans) ? 1 : unfinishedResults.size();
+  }
+
+
+  /**
+   * Tests a result to see it is being resumed.
+   * @param result The Answer to test.
+   * @return <code>true</code> if this result has already been partly resumed.
+   */
+  private boolean isResuming(Answer result) {
+    return (unfinishedResults != null) && unfinishedResults.keySet().contains(result);
+  }
+
+
+  /**
+   * Marks a result as finished.
+   * @param result The Answer that just finished.
+   * @throws TuplesException Error while cleaning up the result.
+   */
+  private void resultFinished(Answer result) throws TuplesException {
+    if (unfinishedResults != null) unfinishedResults.remove(result);
+    result.close();
+  }
+
+
+  /**
+   * Adds an unfinished Answer to the current session for later display. The Answer is not
+   * added if all the rows have been displayed.
+   * @param result The Answer to add to the session.
+   * @param numDisplayed The number of rows that have just been displayed from the result.
+   * @param cmd The Command associated with the Answer in <var>result</var>.
+   * @throws TuplesException If it is not possible to get the number of remaining rows.
+   */
+  private void addUnfinishedResult(Answer result, int numDisplayed, Command cmd) throws TuplesException {
+    // Determine how many results have yet to be displayed from this Answer
+    long remainingRows;
+    if (unfinishedResults == null) remainingRows = result.getRowCount();
+    else {
+      // get the remaining from the session, or use the full value if not in the session
+      Pair<Long,Command> lastRemainingRows = unfinishedResults.get(result);
+      remainingRows = (lastRemainingRows == null) ? result.getRowCount() : lastRemainingRows.first();
+    }
+    // just diplayed a page, so decrement by a page
+    remainingRows -= numDisplayed;
+    if (remainingRows < 0) throw new IllegalStateException("Cannot display more rows than are available.");
+
+    // No more to display, so clean up and leave
+    if (remainingRows == 0) {
+      result.close();
+      unfinishedResults.remove(result);
+      return;
+    }
+
+    // Remember that there is more for this result
+    if (unfinishedResults == null) {
+      unfinishedResults = new LinkedHashMap<Answer,Pair<Long,Command>>();
+      request.getSession().setAttribute(UNFINISHED_RESULTS, unfinishedResults);
+    }
+    unfinishedResults.put(result, new Pair<Long,Command>(remainingRows, cmd));
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/QueryServlet.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/QueryServlet.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/QueryServlet.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,477 @@
+/*
+ * 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.webquery;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.mulgara.connection.Connection;
+import org.mulgara.connection.SessionConnection;
+import org.mulgara.itql.TqlInterpreter;
+import org.mulgara.parser.Interpreter;
+import org.mulgara.parser.MulgaraParserException;
+import org.mulgara.query.Answer;
+import org.mulgara.query.QueryException;
+import org.mulgara.query.TuplesException;
+import org.mulgara.query.operation.Command;
+import org.mulgara.server.AbstractServer;
+import org.mulgara.server.SessionFactory;
+import org.mulgara.util.FnE;
+import org.mulgara.util.C;
+import org.mulgara.util.Pair;
+import org.mulgara.util.StackTrace;
+
+import static org.mulgara.webquery.Template.*;
+
+import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
+import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
+
+/**
+ * A web UI for the server.
+ *
+ * @created Jul 28, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class QueryServlet extends HttpServlet {
+
+  /** Serialization by default */
+  private static final long serialVersionUID = -8407263937557243990L;
+
+  /** The default name to use for the host. */
+  private static final String DEFAULT_HOSTNAME = "localhost";
+
+  /** Session value for database connection. */
+  private static final String CONNECTION = "session.connection";
+
+  /** Session value for interpreter. */
+  private static final String INTERPRETER = "session.interpreter";
+
+  /** The name of the host for the application. */
+  private String hostname;
+
+  /** The name of the server for the application. */
+  private String servername;
+
+  /** The server for finding a session factory. */
+  private AbstractServer server;
+
+  /** Session factory for accessing the database. */
+  private SessionFactory cachedSessionFactory;
+
+  /** The path down to the TEMPLATE resource. */
+  private String templatePath;
+
+  /** The path prefix for resources. */
+  private String resourcePath;
+
+  /** Debugging text. */
+  private String debugText = "";
+
+  /**
+   * Creates the servlet for the named host.
+   * @param hostname The host name to use, or <code>null</code> if this is not known.
+   * @param servername The name of the current server.
+   */
+  public QueryServlet(String hostname, String servername, AbstractServer server) throws IOException {
+    this.hostname = (hostname != null) ? hostname : DEFAULT_HOSTNAME;
+    this.servername = servername;
+    this.cachedSessionFactory = null;
+    this.server = server;
+    URL path = getClass().getClassLoader().getResource(ResourceFile.RESOURCES + TEMPLATE);
+    if (path == null) throw new IOException("Resource not found: " + ResourceFile.RESOURCES + TEMPLATE);
+    templatePath = path.toString();
+    resourcePath = templatePath.split("!")[0];
+  }
+
+
+  /**
+   * Respond to a request for the servlet.
+   * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+   */
+  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+    String path = req.getPathInfo();
+    debugText = path;
+    // case analysis for request type
+    String ext = getExtension(path);
+    if (ext.equals(".jpg") || ext.equals(".png") || ext.equals(".jpeg")) {
+      resp.setContentType("image/jpeg");
+      new ResourceBinaryFile(path).sendTo((OutputStream)resp.getOutputStream());
+    } else if (ext.equals(".css")) {
+      resp.setContentType("text/css");
+      new ResourceBinaryFile(path).sendTo(resp.getOutputStream());
+    } else {
+      // file request
+      resp.setContentType("text/html");
+      resp.setHeader("pragma", "no-cache");
+
+      // check for some parameters
+      String resultOrdinal = req.getParameter(RESULT_ORD_ARG);
+      String queryGetGraph = req.getParameter(MODEL_ARG);
+
+      // return the appropriate page for the given parameters
+      if (resultOrdinal != null) {
+        doNextPage(req, resp, resultOrdinal);
+      } else if (queryGetGraph != null) {
+        doQuery(req, resp, queryGetGraph);
+      } else {
+        clearOldResults(req);
+        outputStandardTemplate(resp);
+      }
+    }
+  }
+
+
+  /**
+   * Respond to a request for the servlet.
+   * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+   */
+  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+    if (!req.getPathInfo().startsWith("/" + EXECUTE_LINK)) {
+      resp.sendError(SC_BAD_REQUEST, "Sent a command to the wrong page.");
+      return;
+    }
+    
+    doQuery(req, resp, req.getParameter(MODEL_ARG));
+  }
+
+
+  /**
+   * Provide a description for the servlet.
+   * @see javax.servlet.GenericServlet#getServletInfo()
+   */
+  public String getServletInfo() {
+    return "Mulgara Query UI";
+  }
+
+
+  /**
+   * Generates the standard page from the template HTML file.
+   * @param resp The object used to respond to the client.
+   * @throws IOException Caused by an error writing to the client.
+   */
+  private void outputStandardTemplate(HttpServletResponse resp) throws IOException {
+    PrintWriter out = resp.getWriter();
+    new ResourceTextFile(TEMPLATE, getTemplateTags()).sendTo(out);
+    out.close();
+  }
+
+
+  /**
+   * Execute the appropriate query, and display the results.
+   * @param req The user request.
+   * @param resp The response object for output to the user.
+   * @param graphUri The URI in a user request.
+   * @throws IOException Error sending a response to the client.
+   */
+  private void doQuery(HttpServletRequest req, HttpServletResponse resp, String graphUri) throws IOException {
+    clearOldResults(req);
+
+    // work out which commands to run
+    String command = generateCommand(req, graphUri);
+
+    // No command to run, so show the entry page
+    if (command == null || command.length() == 0) {
+      outputStandardTemplate(resp);
+      return;
+    }
+
+    // execute all the commands, and accumulate the results
+    List<Object> results = null;
+    List<Command> cmds = null;
+    long time = 0;
+    try {
+      // record how long this takes
+      time = System.currentTimeMillis();
+      final Connection c = getConnection(req);
+      cmds = getInterpreter(req).parseCommands(command);
+      results = C.map(cmds, new FnE<Command,Object,Exception>() { public Object fn(Command cmd) throws Exception { return cmd.execute(c); } });
+      time = System.currentTimeMillis() - time;
+    } catch (MulgaraParserException mpe) {
+      resp.sendError(SC_BAD_REQUEST, "Error parsing command: " + mpe.getMessage());
+      return;
+    } catch (IllegalStateException ise) {
+      resp.sendError(SC_SERVICE_UNAVAILABLE, ise.getMessage());
+      return;
+    } catch (Exception e) {
+      resp.sendError(SC_BAD_REQUEST, "Error executing command: " + StackTrace.throwableToString(e));
+      return;
+    }
+
+    // Get the tags to use in the page template
+    Map<String,String> templateTags = getTemplateTagMap();
+    templateTags.put(GRAPH_TAG, defaultGraph(graphUri));
+    // Generate the page
+    QueryResponsePage page = new QueryResponsePage(req, resp, templateTags);
+    page.writeResults(time, cmds, results);
+  }
+
+
+  /**
+   * Print out the next page of a set of results, specified by the ordinal number
+   * @param req The request environment.
+   * @param resp The response object.
+   * @param ordinalStr The result number to display the next page for.
+   * @throws IOException Error responding to the client.
+   */
+  private void doNextPage(HttpServletRequest req, HttpServletResponse resp, String ordinalStr) throws IOException {
+    // get the session/request parameters, and validate
+    Map<Answer,Pair<Long,Command>> unfinishedResults = getUnfinishedResults(req);
+    int ordinal = 0;
+    try {
+      ordinal = Integer.parseInt(ordinalStr);
+    } catch (NumberFormatException nfe) {
+      ordinal = -1;
+    }
+
+    if (ordinal <= 0 || unfinishedResults == null || ordinal > unfinishedResults.size()) {
+      resp.sendError(SC_BAD_REQUEST, "Result not available. Did you use the \"Back button\"? (result " + ordinalStr +
+                 " of " + ((unfinishedResults == null) ? 0 : unfinishedResults.size()) + ")");
+      clearOldResults(req);
+      return;
+    }
+
+    // Close and remove all the results we don't need
+    Answer remaining = closeExcept(unfinishedResults.keySet(), ordinal);
+    
+    // Get the tags to use in the page template
+    Map<String,String> templateTags = getTemplateTagMap();
+    templateTags.put(GRAPH_TAG, defaultGraph(req.getParameter(MODEL_ARG)));
+    
+    // Generate the page
+    QueryResponsePage page = new QueryResponsePage(req, resp, templateTags);
+    page.writeResult(unfinishedResults.get(remaining).second(), remaining);
+  }
+
+
+  /**
+   * Close all but one answer.
+   * @param answers The answers to close.
+   * @param ordinal The ordinal (1-based) of the answer to NOT close.
+   * @return The Answer that did NOT get removed.
+   */
+  private Answer closeExcept(Set<Answer> answers, int ordinal) {
+    Answer excludedResult = null;
+
+    Iterator<Answer> i = answers.iterator();
+    int nrResults = answers.size();
+    for (int r = 0; r < nrResults; r++) {
+      assert i.hasNext();
+      if (r == ordinal - 1) {
+        excludedResult = i.next();
+      } else {
+        try {
+          i.next().close();
+        } catch (TuplesException e) { /* ignore */ }
+        i.remove();
+      }
+    }
+    assert !i.hasNext();
+    assert excludedResult != null;
+
+    return excludedResult;
+  }
+
+
+  /**
+   * Clears out any old results found in the session.
+   * @param req The current environment.
+   */
+  private void clearOldResults(HttpServletRequest req) {
+    Map<Answer,Pair<Long,Command>> results = getUnfinishedResults(req);
+    try {
+      if (results != null) {
+        for (Answer a: results.keySet()) a.close();
+        results.clear();
+      }
+    } catch (TuplesException e) {
+      // ignoring these problems, since the answer is being thrown away
+    }
+  }
+
+
+  /**
+   * Finds any unfinished data in the current session.
+   * @param req The current request environment.
+   * @return The unfinished results that were recorded in this session, or <code>null</code>
+   *         if there were no unfinished results.
+   */
+  @SuppressWarnings("unchecked")
+  private Map<Answer,Pair<Long,Command>> getUnfinishedResults(HttpServletRequest req) {
+    Map<Answer,Pair<Long,Command>> oldResultData = (Map<Answer,Pair<Long,Command>>)req.getSession().getAttribute(UNFINISHED_RESULTS);
+    return (oldResultData == null) ? null : oldResultData;
+  }
+
+  /**
+   * Analyse the request parameters and work out what kind of query to generate.
+   * Resource and Literal queries are mutually exclusive, and both override
+   * an explicit query argument.
+   * @param req The request environment.
+   * @param graphUri The graphUri set for this request.
+   * @return The command or commands to execute.
+   */
+  private String generateCommand(HttpServletRequest req, String graphUri) {
+    String queryResource = req.getParameter(QUERY_RESOURCE_ARG);
+    if (queryResource != null) return buildResourceQuery(graphUri, queryResource);
+
+    String queryLiteral = req.getParameter(QUERY_LITERAL_ARG);
+    if (queryLiteral != null) return buildLiteralQuery(graphUri, queryLiteral);
+
+    return req.getParameter(QUERY_TEXT_ARG);
+  }
+
+
+  /**
+   * Get a query string to display a resource.
+   * @param graph The name of the graph to get the resource from.
+   * @param resource The name of the resource.
+   * @return The query string.
+   */
+  private String buildResourceQuery(String graph, String resource) {
+    StringBuilder urlString = new StringBuilder("select $Predicate $Object from <");
+    urlString.append(graph).append("> where <").append(resource).append("> $Predicate $Object;");
+    urlString.append("select $Subject $Predicate from <").append(graph);
+    urlString.append("> where $Subject $Predicate <").append(resource).append(">;");
+    urlString.append("select $Subject $Object from <").append(graph);
+    urlString.append("> where $Subject <").append(resource).append("> $Object;");
+    return urlString.toString();
+  }
+
+
+  /**
+   * Get a query string to display a literal.
+   * @param graph The name of the graph to get the resource from.
+   * @param literal The value of the literal.
+   * @return The query string.
+   */
+  private String buildLiteralQuery(String graph, String literal) {
+    return "select $Subject $Predicate  from <" + graph + "> where $Subject $Predicate " + literal + ";";
+  }
+
+
+  /**
+   * Takes each of the template tags and creates a map out of them.
+   * @return A map of all tags to the data to replace them.
+   */
+  private Map<String,String> getTemplateTagMap() {
+    Map<String,String> tagMap = new HashMap<String,String>();
+    String[][] source = getTemplateTags();
+    for (String[] tag: source) tagMap.put(tag[0], tag[1]);
+    return tagMap;
+  }
+
+
+  /**
+   * Gets the list of tags to be replaced in a template document, along with the values
+   * to replace them with.
+   * @return An array of string pairs. The first string in the pair is the tag to replace,
+   *         the second string is the value to repace the tag with.
+   */
+  private String[][] getTemplateTags() {
+    return new String[][] {
+        new String[] {HOSTNAME_TAG, hostname},
+        new String[] {SERVERNAME_TAG, servername},
+        new String[] {JARURL_TAG, resourcePath},
+        new String[] {EXECUTE_TAG, EXECUTE_LINK},
+        new String[] {DEBUG_TAG, debugText},
+    };
+  }
+
+
+  /**
+   * Creates the default graph name for the sample data.
+   * @param The graph name that the user has already set.
+   * @return The default graph name to use when no graph has been set.
+   */
+  private String defaultGraph(String graphParam) {
+    if (graphParam != null && graphParam.length() > 0) return graphParam;
+    return "rmi://" + hostname + "/" + servername + "#sampledata";
+  }
+
+
+  /**
+   * Gets the interpreter for the current session, creating it if it doesn't exist yet.
+   * @param req The current request environment.
+   * @return A connection that is tied to this HTTP session.
+   */
+  private Interpreter getInterpreter(HttpServletRequest req) {
+    HttpSession httpSession = req.getSession();
+    Interpreter interpreter = (Interpreter)httpSession.getAttribute(INTERPRETER);
+    if (interpreter == null) {
+      interpreter = new TqlInterpreter();
+      httpSession.setAttribute(INTERPRETER, interpreter);
+    }
+    return interpreter;
+  }
+
+
+  /**
+   * Gets the connection for the current session, creating it if it doesn't exist yet.
+   * @param req The current request environment.
+   * @return A connection that is tied to this HTTP session.
+   * @throws IOException When an error occurs creating a new session.
+   */
+  private Connection getConnection(HttpServletRequest req) throws IOException, IllegalStateException {
+    HttpSession httpSession = req.getSession();
+    Connection connection = (Connection)httpSession.getAttribute(CONNECTION);
+    if (connection == null) {
+      try {
+        connection = new SessionConnection(getSessionFactory().newSession(), null, null);
+      } catch (QueryException qe) {
+        throw new IOException("Unable to create a connection to the database. " + qe.getMessage());
+      }
+      httpSession.setAttribute(CONNECTION, connection);
+    }
+    return connection;
+  }
+
+
+  /**
+   * This method allows us to put off getting a session factory until the server is
+   * ready to provide one.
+   * @return A new session factory.
+   */
+  private SessionFactory getSessionFactory() throws IllegalStateException {
+    if (cachedSessionFactory == null) {
+      cachedSessionFactory = server.getSessionFactory();
+      if (cachedSessionFactory == null) throw new IllegalStateException("Server not yet ready. Try again soon.");
+    }
+    return cachedSessionFactory;
+  }
+
+
+  /**
+   * Returns the filename extension for a given path.
+   * @param path The path to get the extension for.
+   * @return The extension, including the . character. If there is no extension, then an empty string.
+   */
+  private String getExtension(String path) {
+    int dot = path.lastIndexOf('.');
+    if (dot < 0) return "";
+    return path.substring(dot);
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/RequestException.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/RequestException.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/RequestException.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,43 @@
+/*
+ * 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.webquery;
+
+/**
+ * An internal exception for communicating issues that need to be sent out as HTTP responses.
+ *
+ * @created Aug 7, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class RequestException extends Exception {
+
+  /** The generated serialization ID. */
+  private static final long serialVersionUID = 1578918131388079524L;
+
+  /**
+   * @param message The description of the problem.
+   */
+  public RequestException(String message) {
+    super(message);
+  }
+
+  /**
+   * @param message The description of the problem.
+   * @param cause A throwable that caused the problem.
+   */
+  public RequestException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/ResourceBinaryFile.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/ResourceBinaryFile.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/ResourceBinaryFile.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,102 @@
+/*
+ * 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.webquery;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+
+import javax.servlet.ServletOutputStream;
+
+/**
+ * Loads a resource file, and sends the results to an output stream.
+ * This object closes any streams it receives, since any files written to those
+ * streams are "binary" and are therefore considered complete.
+ *
+ * @created Aug 1, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class ResourceBinaryFile extends ResourceFile {
+
+  /** The default buffer size is 10K. */
+  private static final int BUFFER_SIZE = 10240;
+
+  /** The buffer size to use. */
+  private int bufferSize = BUFFER_SIZE;
+
+  /**
+   * Loads a binary resource file.
+   * @param resourceFile The path of the resource to load.
+   */
+  public ResourceBinaryFile(String resourceFile) {
+    super(resourceFile);
+  }
+
+  /**
+   * Loads a binary resource file.
+   * @param resourceFile The path of the resource to load.
+   */
+  public ResourceBinaryFile(String resourceFile, int bufferSize) {
+    super(resourceFile);
+    this.bufferSize = bufferSize;
+  }
+
+  /**
+   * Sets the buffer size to use when reading/writing resource files.
+   * @param bufferSize The new buffer size to use, in bytes.
+   */
+  public void setBufferSize(int bufferSize) {
+    this.bufferSize = bufferSize;
+  }
+
+  /**
+   * Sends the resource to a given output.
+   * @param out The output that will receive the file.
+   * @return The provided OutputStream.
+   */
+  public OutputStream sendTo(OutputStream out) throws IOException {
+    InputStream in = getStream();
+    try {
+      if (in == null) return out;
+      byte[] buffer = new byte[bufferSize];
+      int r;
+      while ((r = in.read(buffer)) != -1) out.write(buffer, 0, r);
+    } finally {
+      in.close();
+      out.close();
+    }
+    return out;
+  }
+
+  /**
+   * Sends the resource to a given output, using text transfers.
+   * @param out The output that will receive the file.
+   * @return The provided ServletOutputStream.
+   */
+  public ServletOutputStream sendTextTo(ServletOutputStream out) throws IOException {
+    BufferedReader reader = new BufferedReader(new InputStreamReader(getStream()));
+    try {
+      String line;
+      while ((line = reader.readLine()) != null) out.println(line);
+    } finally {
+      reader.close();
+      out.close();
+    }
+    return out;
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/ResourceFile.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/ResourceFile.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/ResourceFile.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,31 @@
+package org.mulgara.webquery;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public abstract class ResourceFile {
+
+  /** The location of resource files. */
+  static final String RESOURCES = "resources";
+  
+  /** The path of the resource file to load. */
+  protected String resourceFile;
+
+  /**
+   * Create the ResourceFile.
+   * @param resourceFile The path to the resource file.
+   */
+  ResourceFile(String resourceFile) {
+    this.resourceFile = RESOURCES + resourceFile;
+  }
+
+  /**
+   * Get the data from the resource file as a stream.
+   * @return An InputStream for accessing the resource file.
+   */
+  protected InputStream getStream() throws IOException {
+    InputStream in = getClass().getClassLoader().getResourceAsStream(resourceFile);
+    if (in == null) throw new IOException("Unable to load resource: " + resourceFile);
+    return in;
+  }
+}
\ No newline at end of file

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/ResourceTextFile.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/ResourceTextFile.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/ResourceTextFile.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,133 @@
+/*
+ * 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.webquery;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.mulgara.util.StackTrace;
+
+/**
+ * Loads a resource file, replaces any tags, and sends the results to an output stream.
+ *
+ * @created Aug 1, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class ResourceTextFile extends ResourceFile {
+
+  /** The marker for tags in a resource file. */
+  private static final String MARKER = "@@";
+
+  /** A mapping of tags in the file to their replacement text. */
+  Map<String,String> tagMap;
+
+  /**
+   * Loads a text file with no tag replacement.
+   * @param resourceFile The path of the resource to load.
+   */
+  public ResourceTextFile(String resourceFile) {
+    super(resourceFile);
+    tagMap = Collections.emptyMap();
+  }
+
+  /**
+   * Loads a text file with a map for replacing tags.
+   * @param resourceFile The path of the resource to load.
+   * @param tagMap A mapping of tags to their replacement text.
+   */
+  public ResourceTextFile(String resourceFile, Map<String,String> tagMap) {
+    super(resourceFile);
+    setTags(tagMap);
+  }
+
+  /**
+   * Loads a text file with a map for replacing tags.
+   * @param resourceFile The path of the resource to load.
+   * @param tagMap Pairs of tagging strings to the values they are to be replaced with.
+   */
+  public ResourceTextFile(String resourceFile, String[][] tagMap) {
+    super(resourceFile);
+    setTags(tagMap);
+  }
+
+  /**
+   * Set the tags and the values they map to.
+   * @param tagMap A map of simple tag strings to the values they are to be replaced with.
+   * @return The current object.
+   */
+  public ResourceFile setTags(Map<String,String> tagMap) {
+    this.tagMap = new HashMap<String,String>();
+    for (Map.Entry<String,String> tag: tagMap.entrySet()) {
+      this.tagMap.put(MARKER + tag.getKey() + MARKER, tag.getValue());
+    }
+    return this;
+  }
+
+  /**
+   * Set the tags and the values they map to.
+   * @param tagPairs Pairs of tagging strings to the values they are to be replaced with.
+   * @return The current object.
+   */
+  public ResourceFile setTags(String[][] tagPairs) {
+    tagMap = new HashMap<String,String>();
+    for (String[] tag: tagPairs) {
+      if (tag.length != 2) throw new IllegalArgumentException("Require pairs of values for mapping tags. Got " + tag.length + " values.");
+      tagMap.put(MARKER + tag[0] + MARKER, tag[1]);
+    }
+    return this;
+  }
+
+  /**
+   * Sends the resource to a print writer after replacing all detected tags.
+   * @param out The PrintWriter that will receive the file.
+   * @return The provided PrintWriter.
+   */
+  public PrintWriter sendTo(PrintWriter out) throws IOException {
+    BufferedReader reader = new BufferedReader(new InputStreamReader(getStream()));
+    String line;
+    while ((line = reader.readLine()) != null) {
+      for (Map.Entry<String,String> tag: tagMap.entrySet()) line = line.replaceAll(tag.getKey(), tag.getValue());
+      out.println(line);
+    }
+    reader.close();
+    return out;
+  }
+
+  /**
+   * Loads the resource, replaces any detected tags, and returns the result as a string.
+   * @return The string containing the resource, with all known tags replaced.
+   * @throws IOException The resource could not be read.
+   */
+  public String toString() {
+    try {
+      BufferedReader reader = new BufferedReader(new InputStreamReader(getStream()));
+      StringBuilder buffer = new StringBuilder(reader.readLine());
+      String line;
+      while ((line = reader.readLine()) != null) {
+        for (Map.Entry<String,String> tag: tagMap.entrySet()) line = line.replaceAll(tag.getKey(), tag.getValue());
+        buffer.append(line).append("\n");
+      }
+      reader.close();
+      return buffer.toString();
+    } catch (IOException ioe) {
+      return StackTrace.throwableToString(ioe);
+    }
+  }
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/Template.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/Template.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/Template.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,85 @@
+/*
+ * 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.webquery;
+
+/**
+ * Contains data for the template responses.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class Template {
+
+  /** Session variable for the set of results that have not been finished. */
+  public static final String UNFINISHED_RESULTS = "unfinishedRsults";
+
+  /** The name of the template HTML file. */
+  public static final String TEMPLATE = "/template.html";
+  
+  /** The name of the HTML file containing the head of a result. */
+  public static final String TEMPLATE_HEAD = "/template_head.html";
+  
+  /** The name of the HTML file containing the tail of a result. */
+  public static final String TEMPLATE_TAIL = "/template_tail.html";
+  
+  /** The tag to replace in the the template file for the host name. */
+  public static final String HOSTNAME_TAG = "hostname";
+
+  /** The tag to replace in the the template file for the server name. */
+  public static final String SERVERNAME_TAG = "servername";
+
+  /** The tag to replace in the the template file for the jar file path. */
+  public static final String JARURL_TAG = "jarurl";
+
+  /** The tag to replace in the the template file for the execute link. */
+  public static final String EXECUTE_TAG = "execute";
+
+  /** The tag to replace in the the template file for debugging info. */
+  public static final String DEBUG_TAG = "debug";
+
+  /** The tag to replace in the template file for the user-set graph URI. */
+  public static final String GRAPH_TAG = "graph";
+
+  /** The amount of indenting to use for the rows in the template. */
+  public static final int ROW_INDENT = 8;
+
+  /** The link used for executing a query. */
+  public static final String EXECUTE_LINK = "ExecuteQuery";
+
+  /** The name of the field holding the query text to execute. */
+  public static final String QUERY_TEXT_ARG = "QueryText";
+
+  /** The ModelURI argument for URL parameters. */
+  public static final String MODEL_ARG = "ModelURI";
+
+  /** The QueryResource argument for URL parameters. */
+  public static final String QUERY_RESOURCE_ARG = "QueryResource";
+
+  /** The QueryLiteral argument for URL parameters. */
+  public static final String QUERY_LITERAL_ARG = "QueryLiteral";
+
+  /** The ResultOrdinal argument for URL parameters. */
+  public static final String RESULT_ORD_ARG = "ResultOrdinal";
+
+  /** A label out of the CSS file. That file could be structurally generated, but for no good reason. */
+  public static final String CSS_LARGE = "styleLarge";
+
+  /** A CSS label for cells holding a result table. */
+  public static final String CSS_RESULT_TABLE_CELL = "resultTableCell";
+
+  /** A CSS label for  a result table. */
+  public static final String CSS_RESULT_TABLE = "resultTable";
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/Anchor.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/Anchor.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/Anchor.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,166 @@
+/*
+ * 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.webquery.html;
+
+import java.net.URI;
+import java.net.URL;
+
+/**
+ * Represents an Anchor element. The constructors expect an HREF or a name.
+ * The constructors ar duplicated between URL and URI parameters, as URL
+ * objects do not hold relative URLs, while URIs can.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class Anchor extends HtmlElement {
+
+  /**
+   * Creates an anchor with a given indent.
+   * @param indent The amount to indent by.
+   * @param ref The reference to point to.
+   * @param subElements The list of subElements to embed. Probably just a {@link Text}
+   */
+  public Anchor(int indent, URL ref, HtmlElement... subElements) {
+    super(indent, subElements);
+    this.addAttr(Attr.HREF, ref);
+  }
+
+
+  /**
+   * Creates an anchor with no indenting.
+   * @param ref The reference to point to.
+   * @param subElements The list of subElements to embed. Probably just a {@link Text}
+   */
+  public Anchor(URL ref, HtmlElement... subElements) {
+    this(-1, ref, subElements);
+  }
+
+
+  /**
+   * Creates an anchor with a given indent.
+   * @param indent The amount to indent by.
+   * @param ref The reference to point to.
+   * @param subElements The list of subElements to embed. Probably just a {@link Text}
+   */
+  public Anchor(int indent, URI ref, HtmlElement... subElements) {
+    super(indent, subElements);
+    this.addAttr(Attr.HREF, ref);
+  }
+
+
+  /**
+   * Creates an anchor with no indenting.
+   * @param ref The reference to point to.
+   * @param subElements The list of subElements to embed. Probably just a {@link Text}
+   */
+  public Anchor(URI ref, HtmlElement... subElements) {
+    this(-1, ref, subElements);
+  }
+
+
+  /**
+   * Creates an anchor with a given indent, and a list of sub elements.
+   * @param indent The amount to indent by.
+   * @param name The name of the anchor.
+   * @param subElements a list of sub elements inside this anchor.
+   */
+  public Anchor(int indent, String name, HtmlElement... subElements) {
+    super(indent, subElements);
+    this.addAttr(Attr.NAME, name);
+  }
+
+
+  /**
+   * Creates an anchor with no initial indenting, and a list of sub elements.
+   * @param name The name of the anchor.
+   * @param subElements a list of sub elements inside this anchor.
+   */
+  public Anchor(String name, HtmlElement... subElements) {
+    this(-1, name, subElements);
+  }
+
+
+  /**
+   * Creates an anchor with a given indent, and the text for a subelement.
+   * @param indent The amount to indent by.
+   * @param name The name of the anchor.
+   * @param test The text for a Text subElement.
+   */
+  public Anchor(int indent, String name, String text) {
+    super(indent, new Text(text));
+    this.addAttr(Attr.NAME, name);
+  }
+
+
+  /**
+   * Creates an anchor with no initial indenting, and the text for a subelement.
+   * @param name The name of the anchor.
+   * @param test The text for a Text subElement.
+   */
+  public Anchor(String name, String text) {
+    this(-1, name, new Text(text));
+  }
+
+
+  /**
+   * Creates an anchor with a given indent, and the text for a subelement.
+   * @param indent The amount to indent by.
+   * @param ref The reference to point to.
+   * @param test The text for a Text subElement.
+   */
+  public Anchor(int indent, URL ref, String text) {
+    super(indent, new Text(text));
+    this.addAttr(Attr.HREF, ref);
+  }
+
+
+  /**
+   * Creates an anchor with no initial indenting, the text for a subelement.
+   * @param ref The reference to point to.
+   * @param test The text for a Text subElement.
+   */
+  public Anchor(URL ref, String text) {
+    this(-1, ref, new Text(text));
+  }
+
+
+  /**
+   * Creates an anchor with a given indent, and the text for a subelement.
+   * @param indent The amount to indent by.
+   * @param ref The reference to point to.
+   * @param test The text for a Text subElement.
+   */
+  public Anchor(int indent, URI ref, String text) {
+    super(indent, new Text(text));
+    this.addAttr(Attr.HREF, ref);
+  }
+
+
+  /**
+   * Creates an anchor with no initial indenting, and the text for a subelement.
+   * @param ref The reference to point to.
+   * @param test The text for a Text subElement.
+   */
+  public Anchor(URI ref, String text) {
+    this(-1, ref, new Text(text));
+  }
+
+
+  protected String getTag() {
+    return "a";
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/Break.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/Break.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/Break.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,66 @@
+/*
+ * 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.webquery.html;
+
+/**
+ * Represents a line break.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class Break extends HtmlElement {
+
+  /**
+   * Creates a line break with a given indent.
+   * @param indent The amount to indent by.
+   */
+  public Break(int indent) {
+    super(indent);
+  }
+
+
+  /**
+   * Creates a line break with no indenting.
+   */
+  Break() {
+    this(0);
+  }
+
+  /**
+   * @throws UnsupportedOperationException As sub elements are not possible for this element. 
+   * @see org.mulgara.webquery.html.HtmlElement#add(org.mulgara.webquery.html.HtmlElement)
+   */
+  public HtmlElement add(HtmlElement elt) {
+    throw new UnsupportedOperationException("Line breaks do not have sub elements.");
+  }
+
+
+  /**
+   * @see org.mulgara.webquery.html.HtmlElement#addAttr(org.mulgara.webquery.html.HtmlElement.Attr, java.lang.String)
+   */
+  public HtmlElement addAttr(Attr attr, String val) {
+    if (attr != Attr.ID && attr != Attr.CLASS && attr != Attr.STYLE && attr != Attr.TITLE) {
+      throw new UnsupportedOperationException("Line breaks do not have attributes.");
+    }
+    return super.addAttr(attr, val);
+  }
+
+
+
+  protected String getTag() {
+    return "br";
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/Div.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/Div.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/Div.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,72 @@
+/*
+ * 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.webquery.html;
+
+/**
+ * Represents a div element. This implementation always uses an id attribute.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class Div extends HtmlElement {
+
+  /**
+   * Creates a div with a given indent.
+   * @param indent The amount to indent by.
+   * @param id The name of the id for the div.
+   */
+  public Div(int indent, String id) {
+    super(indent);
+    this.addAttr(Attr.CLASS, id);
+  }
+
+
+  /**
+   * Creates a div with no indenting.
+   * @param id The name of the id for the div.
+   */
+  Div(String id) {
+    this(0, id);
+  }
+
+
+  /**
+   * Creates a div with a given indent, and a list of sub elements.
+   * @param indent The amount to indent by.
+   * @param id The name of the id for the div.
+   * @param subElements a list of sub elements inside this div.
+   */
+  Div(int indent, String id, HtmlElement... subElements) {
+    super(indent, subElements);
+    this.addAttr(Attr.CLASS, id);
+  }
+
+
+  /**
+   * Creates a div with no initial indenting, and a list of sub elements.
+   * @param id The name of the id for the div.
+   * @param subElements a list of sub elements inside this div.
+   */
+  Div(String id, HtmlElement... subElements) {
+    this(0, id, subElements);
+  }
+
+
+  /** @see org.mulgara.webquery.html.HtmlElement#getTag() */
+  protected String getTag() {
+    return "div";
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/Emphasis.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/Emphasis.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/Emphasis.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,55 @@
+/*
+ * 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.webquery.html;
+
+/**
+ * Represents an emphasis element.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class Emphasis extends HtmlElement {
+
+  /**
+   * Creates an emphasis element.
+   */
+  public Emphasis() {
+    super(-1);
+  }
+
+
+  /**
+   * Creates an emphasis element with embedded text.
+   * @param text The text to embed.
+   */
+  public Emphasis(String text) {
+    super(-1, new Text(text));
+  }
+
+
+  /**
+   * Always returns false, indicating that formatting is never used on this element.
+   * @see org.mulgara.webquery.html.HtmlElement#shouldIndent()
+   */
+  protected boolean shouldIndent() {
+    return false;
+  }
+
+
+  protected String getTag() {
+    return "em";
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/HtmlElement.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/HtmlElement.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/HtmlElement.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,228 @@
+/*
+ * 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.webquery.html;
+
+import java.io.PrintWriter;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.mulgara.util.C;
+
+/**
+ * Represents an HTML element that can be written to an output stream.
+ * Indents are hard coded to 2 characters.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public abstract class HtmlElement {
+
+  /** The amount to indent by. */
+  private static final String INDENT_CHARS = "  ";
+
+  /** The number of indents to use for this element. */
+  protected int indent;
+
+  /** The current indent string, where indentStr = {@link #INDENT_CHARS}*{@link #indent}. */
+  private String indentStr;
+
+  /** The list of elements under this element */
+  protected List<HtmlElement> subElements;
+
+  /** The attributes for this element */
+  protected Map<Attr,String> attributes;
+
+  /** A limited list of attributes. Expand on this as required. */
+  public enum Attr {
+    CLASS,
+    ID,
+    STYLE,
+    TITLE,
+    NAME,
+    HEIGHT,
+    WIDTH,
+    SIZE,
+    ALIGN,
+    VALIGN,
+    BORDER,
+    CELLPADDING,
+    CELLSPACING,
+    COLS,
+    ROWS,
+    COLSPAN,
+    ALT,
+    HREF,
+    SELECTED,
+    VALUE,
+    METHOD,
+    SUMMARY;
+
+    public String toString() { return this.name().toLowerCase(); };
+  };
+
+  /** A limited list of entities. Expand on this as required. */
+  public enum Entity {
+    AMP("&amp;"),
+    GT("&gt;"),
+    LT("&lt;"),
+    QUOT("&quot;"),
+    APOS("&#39;"),  // &apos; does not work in IE
+    NBSP("&nbsp;");
+
+    private final String t;
+    private Entity(String t) { this.t = t; }
+    public String toString() { return t; }
+  };
+
+  /**
+   * Creates an empty element with a given indent.
+   * @param indent The amount to indent by.
+   */
+  HtmlElement(int indent) {
+    this.indent = indent;
+    indentStr = generateIndent(indent);
+    subElements = new LinkedList<HtmlElement>();
+    attributes = new LinkedHashMap<Attr,String>();
+  }
+
+
+  /**
+   * Creates an empty element with no initial indenting.
+   */
+  HtmlElement() {
+    this(0);
+  }
+
+
+  /**
+   * Creates an element with a given indent, and a list of sub-elements.
+   * @param indent The amount to indent by.
+   * @param subElements a list of elements directly subordinate to this element.
+   */
+  HtmlElement(int indent, HtmlElement... subElements) {
+    this(indent);
+    for (HtmlElement e: subElements) add(e);
+  }
+
+
+  /**
+   * Creates an empty element with no indenting.
+   * @param subElements a list of elements directly subordinate to this element.
+   */
+  HtmlElement(HtmlElement... subElements) {
+    this(0, subElements);
+  }
+
+
+  /**
+   * Adds a sub element to the end of the list of sub elements for this element.
+   * @param elt The new sub element to be added.
+   * @return The current element.
+   */
+  public HtmlElement add(HtmlElement elt) {
+    subElements.add(elt);
+    elt.setIndent(indent < 0 ? indent : indent + 1);
+    return this;
+  }
+
+
+  /**
+   * Adds an attribute to the set of attributes. This will replace any existing values
+   * for this attribute.
+   * @param attr The attribute to add.
+   * @param val The value for the attribute.
+   * @return The current element.
+   */
+  public HtmlElement addAttr(Attr attr, Object val) {
+    attributes.put(attr, val.toString());
+    return this;
+  }
+
+
+  /**
+   * This method sends the given text to the output stream.
+   * For indented text, this is always called when a new line has just been started.
+   * The method always finishes while at the end of a line.
+   * @param out the writer to send the HTML to.
+   */
+  public void sendTo(PrintWriter out) {
+    if (shouldIndent()) out.append(indentStr);
+    out.append('<').append(getTag());
+    for (Map.Entry<Attr,String> attr: attributes.entrySet()) out.append(' ').append(attr.getKey().toString()).append("=\"").append(attr.getValue()).append('\"');
+    if (subElements.isEmpty()) {
+      // no sub elements, so close the tag
+      out.append("/>");
+    } else {
+      out.append('>');
+      // iterate over the sub elements
+      for (HtmlElement e: subElements) {
+        if (e.shouldIndent()) out.append('\n');
+        e.sendTo(out);
+      }
+      if (C.tail(subElements).shouldIndent()) out.append('\n').append(indentStr);
+      out.append("</").append(getTag()).append('>');
+    }
+  }
+
+
+  /**
+   * Tests if this element should be indented in the current output.
+   * @return <code>true</code> if indenting should occur.
+   */
+  protected boolean shouldIndent() {
+    return indentStr != null;
+  }
+
+
+  /**
+   * Sets the indent of this element. Used by the parent.
+   * @param indent The new indent to use.
+   */
+  private void setIndent(int indent) {
+    this.indent = indent;
+    if (indent < 0) {
+      indentStr = null;
+      for (HtmlElement e: subElements) e.setIndent(indent);
+    } else {
+      indentStr = generateIndent(indent);
+      for (HtmlElement e: subElements) e.setIndent(indent + 1);
+    }
+  }
+
+
+  /**
+   * Create a string of spaces for indenting.
+   * @param i The number of indents to use.
+   * @return A string of spaces, equal to i * {@link #INDENT_CHARS}, or <code>null</code>
+   *         if i < 0.
+   */
+  private static String generateIndent(int i) {
+    if (i < 0) return null;
+    StringBuilder s = new StringBuilder();
+    while (--i > 0) s.append(INDENT_CHARS);
+    return s.toString();
+  }
+
+
+  /**
+   * Any implementing classes must implement this method to create the text representation
+   * of the tag in the emitted HTML.
+   * @return The string for this element.
+   */
+  protected abstract String getTag();
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/Paragraph.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/Paragraph.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/Paragraph.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,65 @@
+/*
+ * 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.webquery.html;
+
+/**
+ * Represents a paragraph element.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class Paragraph extends HtmlElement {
+
+  /**
+   * Creates a paragraph with a given indent.
+   * @param indent The amount to indent by.
+   */
+  public Paragraph(int indent) {
+    super(indent);
+  }
+
+
+  /**
+   * Creates a paragraph with no indenting.
+   */
+  Paragraph() {
+    this(-1);
+  }
+
+
+  /**
+   * Creates a paragraph with a given indent, and a list of sub elements.
+   * @param indent The amount to indent by.
+   * @param subElements a list of sub elements inside this paragraph.
+   */
+  Paragraph(int indent, HtmlElement... subElements) {
+    super(indent, subElements);
+  }
+
+
+  /**
+   * Creates a paragraph with no initial indenting, and a list of sub elements.
+   * @param subElements a list of sub elements inside this paragraph.
+   */
+  Paragraph( HtmlElement... subElements) {
+    this(-1, subElements);
+  }
+
+
+  protected String getTag() {
+    return "p";
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/Span.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/Span.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/Span.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,93 @@
+/*
+ * 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.webquery.html;
+
+/**
+ * Represents a span element. This implementation always uses a class attribute.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class Span extends HtmlElement {
+
+  /**
+   * Creates a span with a given indent.
+   * @param indent The amount to indent by.
+   * @param spanClass The name of the class for the span.
+   */
+  public Span(int indent, String spanClass) {
+    super(indent);
+    this.addAttr(Attr.CLASS, spanClass);
+  }
+
+
+  /**
+   * Creates a span with no indenting.
+   * @param spanClass The name of the class for the span.
+   */
+  public Span(String spanClass) {
+    this(-1, spanClass);
+  }
+
+
+  /**
+   * Creates a span with a given indent, and a list of sub elements.
+   * @param indent The amount to indent by.
+   * @param spanClass The name of the class for the span.
+   * @param subElements a list of sub elements inside this span.
+   */
+  public Span(int indent, String spanClass, HtmlElement... subElements) {
+    super(indent, subElements);
+    this.addAttr(Attr.CLASS, spanClass);
+  }
+
+
+  /**
+   * Creates a span with no initial indenting, and a list of sub elements.
+   * @param spanClass The name of the class for the span.
+   * @param subElements a list of sub elements inside this span.
+   */
+  public Span(String spanClass, HtmlElement... subElements) {
+    this(-1, spanClass, subElements);
+  }
+
+
+  /**
+   * Creates a span with a given indent, and a list of sub elements.
+   * @param indent The amount to indent by.
+   * @param spanClass The name of the class for the span.
+   * @param test The text for a Text subElement.
+   */
+  public Span(int indent, String spanClass, String text) {
+    super(indent, new Text(text));
+    this.addAttr(Attr.CLASS, spanClass);
+  }
+
+
+  /**
+   * Creates a span with no initial indenting, and a list of sub elements.
+   * @param spanClass The name of the class for the span.
+   * @param test The text for a Text subElement.
+   */
+  public Span(String spanClass, String text) {
+    this(-1, spanClass, new Text(text));
+  }
+
+
+  protected String getTag() {
+    return "span";
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/Strong.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/Strong.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/Strong.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,55 @@
+/*
+ * 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.webquery.html;
+
+/**
+ * Represents a strong element.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class Strong extends HtmlElement {
+
+  /**
+   * Creates an empty strong element.
+   */
+  public Strong() {
+    super(-1);
+  }
+
+
+  /**
+   * Creates a strong element with embedded text.
+   * @param text The text to embed.
+   */
+  public Strong(String text) {
+    super(-1, new Text(text));
+  }
+
+
+  /**
+   * Always returns false, indicating that formatting is never used on this element.
+   * @see org.mulgara.webquery.html.HtmlElement#shouldIndent()
+   */
+  protected boolean shouldIndent() {
+    return false;
+  }
+
+
+  protected String getTag() {
+    return "strong";
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/Table.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/Table.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/Table.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,78 @@
+/*
+ * 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.webquery.html;
+
+/**
+ * Represents a table.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class Table extends TableAbstr {
+
+  /**
+   * Creates a table with a given indent.
+   * @param indent The amount to indent by.
+   */
+  public Table(int indent) {
+    super(indent);
+  }
+
+
+  /**
+   * Creates a table with no indenting.
+   */
+  public Table() {
+    super();
+  }
+
+
+  /**
+   * Creates a table with a given indent, and a list of sub elements.
+   * @param indent The amount to indent by.
+   * @param subElements a list of sub elements inside this table.
+   */
+  public Table(int indent, HtmlElement... subElements) {
+    super(indent, subElements);
+  }
+
+
+  /**
+   * Creates a table with no initial indenting, and a list of sub elements.
+   * @param subElements a list of sub elements inside this table.
+   */
+  public Table(HtmlElement... subElements) {
+    super(subElements);
+  }
+
+
+  /**
+   * Adds a table element to the end of the list of table elements for this table.
+   * @param elt The new element to be added.
+   * @return The current table.
+   * @throws IllegalArgumentException If elt is anything other than a table structure.
+   */
+  public HtmlElement add(HtmlElement elt) {
+    if (elt instanceof Table) throw new IllegalArgumentException("Tables can only be embedded in cell, not directly into table elements");
+    return super.add(elt);
+  }
+
+
+  /** @see org.mulgara.webquery.html.HtmlElement#getTag() */
+  protected String getTag() {
+    return "table";
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableAbstr.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableAbstr.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableAbstr.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -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.webquery.html;
+
+/**
+ * An abstraction of a table.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public abstract class TableAbstr extends HtmlElement implements TableStructure {
+
+  /** The width of this table structure */
+  private int width = 0;
+
+
+  /**
+   * Creates a table with a given indent.
+   * @param indent The amount to indent by.
+   */
+  TableAbstr(int indent) {
+    super(indent);
+  }
+
+
+  /**
+   * Creates a table with no indenting.
+   */
+  TableAbstr() {
+    this(0);
+  }
+
+
+  /**
+   * Creates a table with a given indent, and a list of sub elements.
+   * @param indent The amount to indent by.
+   * @param subElements a list of sub elements inside this table cell.
+   */
+  TableAbstr(int indent, HtmlElement... subElements) {
+    super(indent, subElements);
+    for (HtmlElement e: subElements) if (e instanceof TableStructure) updateWidth((TableStructure)e);
+  }
+
+
+  /**
+   * Creates a table body with no initial indenting, and a list of sub elements.
+   * @param subElements a list of sub elements inside this table cell.
+   */
+  TableAbstr(HtmlElement... subElements) {
+    this(0, subElements);
+  }
+
+
+  /**
+   * Adds a table element to the end of the list of table elements for this table.
+   * @param elt The new element to be added.
+   * @return The current table.
+   * @throws IllegalArgumentException If elt is anything other than a table structure.
+   */
+  public HtmlElement add(HtmlElement elt) {
+    if (!(elt instanceof TableStructure)) throw new IllegalArgumentException("Tables can only add table structural elements");
+    updateWidth((TableStructure)elt);
+    return super.add(elt);
+  }
+
+
+  /**
+   * Gets the width of this table body.
+   * @return The maximum width in this table structure.
+   */
+  public int getWidth() {
+    return width;
+  }
+
+
+  /** @see org.mulgara.webquery.html.HtmlElement#getTag() */
+  abstract protected String getTag();
+
+
+  /**
+   * Update the width of this structure to its widest element.
+   * @param s A new element that has been added.
+   */
+  private void updateWidth(TableStructure s) {
+    int w = s.getWidth();
+    if (w > width) width = w;
+  }
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableBody.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableBody.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableBody.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,78 @@
+/*
+ * 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.webquery.html;
+
+/**
+ * Represents a table body.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class TableBody extends TableAbstr {
+
+  /**
+   * Creates a table body with a given indent.
+   * @param indent The amount to indent by.
+   */
+  public TableBody(int indent) {
+    super(indent);
+  }
+
+
+  /**
+   * Creates a table body with no indenting.
+   */
+  public TableBody() {
+    super();
+  }
+
+
+  /**
+   * Creates a table body with a given indent, and a list of sub elements.
+   * @param indent The amount to indent by.
+   * @param subElements a list of sub elements inside this table cell.
+   */
+  public TableBody(int indent, HtmlElement... subElements) {
+    super(indent, subElements);
+  }
+
+
+  /**
+   * Creates a table body with no initial indenting, and a list of sub elements.
+   * @param subElements a list of sub elements inside this table cell.
+   */
+  public TableBody(HtmlElement... subElements) {
+    super(subElements);
+  }
+
+
+  /**
+   * Adds a table element to the end of the list of table elements for this table.
+   * @param elt The new element to be added.
+   * @return The current table.
+   * @throws IllegalArgumentException If elt is anything other than a table structure.
+   */
+  public HtmlElement add(HtmlElement elt) {
+    if (!(elt instanceof TableRow)) throw new IllegalArgumentException("Table bodies can only add table rows");
+    return super.add(elt);
+  }
+
+
+  /** @see org.mulgara.webquery.html.HtmlElement#getTag() */
+  protected String getTag() {
+    return "tbody";
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableData.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableData.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableData.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,85 @@
+/*
+ * 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.webquery.html;
+
+/**
+ * Represents data in a table.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class TableData extends HtmlElement {
+
+  /**
+   * Creates a data element with a given indent.
+   * @param indent The amount to indent by.
+   */
+  public TableData(int indent) {
+    super(indent);
+  }
+
+
+  /**
+   * Creates a data element with no indenting.
+   */
+  public TableData() {
+    this(0);
+  }
+
+
+  /**
+   * Creates a data element with a given indent, and a list of sub elements.
+   * @param indent The amount to indent by.
+   * @param subElements a list of sub elements inside this table cell.
+   */
+  public TableData(int indent, HtmlElement... subElements) {
+    super(indent, subElements);
+  }
+
+
+  /**
+   * Creates a data element with no initial indenting, and a list of sub elements.
+   * @param subElements a list of sub elements inside this table cell.
+   */
+  public TableData(HtmlElement... subElements) {
+    this(0, subElements);
+  }
+
+
+  /**
+   * Creates a data element with a given indent, and embedded text.
+   * @param indent The amount to indent by.
+   * @param text The text to set for this element.
+   */
+  public TableData(int indent, String text) {
+    super(indent, new Text(text));
+  }
+
+
+  /**
+   * Creates a data element with no initial indenting, and embedded text.
+   * @param text The text to set for this element.
+   */
+  public TableData(String text) {
+    this(0, new Text(text));
+  }
+
+
+  /** @see org.mulgara.webquery.html.HtmlElement#getTag() */
+  protected String getTag() {
+    return "td";
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableHeader.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableHeader.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableHeader.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,85 @@
+/*
+ * 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.webquery.html;
+
+/**
+ * Represents header data in a table.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class TableHeader extends TableData {
+
+  /**
+   * Creates a header element with a given indent.
+   * @param indent The amount to indent by.
+   */
+  public TableHeader(int indent) {
+    super(indent);
+  }
+
+
+  /**
+   * Creates a header element with no indenting.
+   */
+  public TableHeader() {
+    super();
+  }
+
+
+  /**
+   * Creates a header element with a given indent, and a list of sub elements.
+   * @param indent The amount to indent by.
+   * @param subElements a list of sub elements inside this header cell.
+   */
+  public TableHeader(int indent, HtmlElement... subElements) {
+    super(indent, subElements);
+  }
+
+
+  /**
+   * Creates a header element with no initial indenting, and a list of sub elements.
+   * @param subElements a list of sub elements inside this header cell.
+   */
+  public TableHeader(HtmlElement... subElements) {
+    super(subElements);
+  }
+
+
+  /**
+   * Creates a header element with a given indent, and embedded text.
+   * @param indent The amount to indent by.
+   * @param text The text to set for this header.
+   */
+  public TableHeader(int indent, String text) {
+    super(indent, text);
+  }
+
+
+  /**
+   * Creates a header element with no initial indenting, and embedded text.
+   * @param text The text to set for this header.
+   */
+  public TableHeader(String text) {
+    super(text);
+  }
+
+
+  /** @see org.mulgara.webquery.html.HtmlElement#getTag() */
+  protected String getTag() {
+    return "th";
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableRow.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableRow.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableRow.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,88 @@
+/*
+ * 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.webquery.html;
+
+/**
+ * Represents a row in a table. Any child elements will be wrapped in TableData elements
+ * before being added.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class TableRow extends HtmlElement implements TableStructure {
+
+  /**
+   * Creates a row with a given indent.
+   * @param indent The amount to indent by.
+   */
+  public TableRow(int indent) {
+    super(indent);
+  }
+
+
+  /**
+   * Creates a row with no indenting.
+   */
+  public TableRow() {
+    this(0);
+  }
+
+
+  /**
+   * Creates a row with a given indent, and a list of data elements.
+   * @param indent The amount to indent by.
+   * @param rowData a list of data elements to go inside this row.
+   */
+  public TableRow(int indent, HtmlElement... rowData) {
+    super(indent, rowData);
+  }
+
+
+  /**
+   * Creates an row with no initial indenting, and a list of data elements.
+   * @param subElements a list of data elements to go inside this row.
+   */
+  public TableRow(HtmlElement... rowData) {
+    this(0, rowData);
+  }
+
+
+  /**
+   * Adds a sub element to the end of the list of sub elements for this element.
+   * @param elt The new sub element to be added.
+   * @return The current element.
+   * @throws IllegalArgumentException If elt is anything other than TableData.
+   */
+  public HtmlElement add(HtmlElement elt) {
+    if (!(elt instanceof TableData)) elt = new TableData(elt);
+    return super.add(elt);
+  }
+
+
+  /**
+   * Gets the width of this row.
+   * @return The number of TD elements in this row.
+   */
+  public int getWidth() {
+    return subElements.size();
+  }
+
+
+  /** @see org.mulgara.webquery.html.HtmlElement#getTag() */
+  protected String getTag() {
+    return "tr";
+  }
+
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableStructure.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableStructure.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/TableStructure.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,30 @@
+/*
+ * 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.webquery.html;
+
+/**
+ * Describes some part of a table's structure.
+ *
+ * @created Aug 8, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public interface TableStructure {
+
+  /**
+   * Returns the width of this part of the structure.
+   * @return The width of this structure.
+   */
+  public int getWidth();
+}

Added: trunk/src/jar/webquery/java/org/mulgara/webquery/html/Text.java
===================================================================
--- trunk/src/jar/webquery/java/org/mulgara/webquery/html/Text.java	                        (rev 0)
+++ trunk/src/jar/webquery/java/org/mulgara/webquery/html/Text.java	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,87 @@
+/*
+ * 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.webquery.html;
+
+import java.io.PrintWriter;
+
+import org.mulgara.util.StringUtil;
+
+/**
+ * Represents free floating text. Indent is irrelevant for this class.
+ *
+ * @created Aug 4, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class Text extends HtmlElement {
+
+  private StringBuilder buffer;
+
+  /**
+   * Creates a text element with no initial text.
+   */
+  public Text() {
+    super(-1);
+    buffer = new StringBuilder();
+  }
+
+
+  /**
+   * Creates a text element from an arbitrary object.
+   * @param obj The object to convert to text for this element.
+   */
+  public Text(Object obj) {
+    super(-1);
+    buffer = new StringBuilder(StringUtil.quoteAV(obj.toString()));
+  }
+
+
+  /**
+   * Appends new text from an arbitrary object to this element.
+   * @param obj The object to convert to text to append to this element.
+   * @return The current instance.
+   */
+  public Text append(Object obj) {
+    buffer.append(StringUtil.quoteAV(obj.toString()));
+    return this;
+  }
+
+
+  /**
+   * Not used for this implementation.
+   * @see org.mulgara.webquery.html.HtmlElement#getTag()
+   */
+  protected String getTag() {
+    return "";
+  }
+
+
+  /**
+   * Always returns false, indicating that formatting is never used on this element.
+   * @see org.mulgara.webquery.html.HtmlElement#shouldIndent()
+   */
+  protected boolean shouldIndent() {
+    return false;
+  }
+
+
+  /**
+   * Appends this text raw to the writer. There are no tags, or formating.
+   * @see org.mulgara.webquery.html.HtmlElement#sendTo(java.io.PrintWriter)
+   */
+  public void sendTo(PrintWriter out) {
+    out.append(buffer);
+  }
+
+}

Added: trunk/src/jar/webquery/resources/debug.html
===================================================================
--- trunk/src/jar/webquery/resources/debug.html	                        (rev 0)
+++ trunk/src/jar/webquery/resources/debug.html	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//Dtd XHTML 1.0 Transitional//EN" "http://www.w3.org/tr/xhtml1/Dtd/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  <title>Mulgara Debugger</title>
+  <link href="styles/ui.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+  <div id="wrapper">
+    <div id="header">
+      <div id="logo">
+        <a href="http://mulgara.org/"><img src="images/logo.jpg" alt="Mulgara - Semantic Store" border="0" /></a>
+      </div>
+    </div>
+    <div id="content">
+      <div id="columnLeft">
+        <h1>Debug Output:</h1>
+@@debug@@
+      </div>
+    </div>
+  </div>
+</body>
+</html>

Added: trunk/src/jar/webquery/resources/images/logo.jpg
===================================================================
(Binary files differ)


Property changes on: trunk/src/jar/webquery/resources/images/logo.jpg
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/src/jar/webquery/resources/styles/ui.css
===================================================================
--- trunk/src/jar/webquery/resources/styles/ui.css	                        (rev 0)
+++ trunk/src/jar/webquery/resources/styles/ui.css	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,133 @@
+ at charset "utf-8";
+* {
+  padding:0;
+  margin:0;
+}
+body {
+  font-family: "Trebuchet MS", Arial;
+  font-size: 12px;
+  color: #000000;
+  margin: 0px;
+  padding: 0px;
+  background-color: #FFFFFF;
+}
+a {
+  color: #0066cc;
+}
+a:hover {
+  text-decoration: none;
+}
+a:visited {
+  color: #330066;
+}
+
+#wrapper {
+  width: 1013px;
+  margin-right: auto;
+  margin-left: auto;
+  background-color: #FFFFFF;
+  padding-right: 8px;
+  padding-left: 8px;
+  background-repeat: repeat-y;
+}
+#header {
+  background-image: url(../images/logo.jpg);
+  background-repeat: no-repeat;
+  background-position: left;
+  height: 109px;
+}
+#content {
+  padding-top: 18px;
+}
+#content h1 {
+  font-style: italic;
+  border-bottom-width: 1px;
+  border-bottom-style: dotted;
+  border-bottom-color: #999999;
+  padding-bottom: 15px;
+}
+#content h2 {
+  padding-top: 8px;
+}
+.clearer {
+  clear: both;
+}
+#logo {
+  float: left;
+}
+#content  p {
+  margin-top: 15px;
+}
+#content  ul {
+  margin-top: 15px;
+  margin-left: 15px;
+}
+#content  td {
+  padding-left: 5px;
+  padding-right: 5px;
+}
+#content  tr {
+  spacing: 15px;
+}
+.styleBlack {
+  color: #FFFFFF
+}
+.styleLB {
+  font-size: 14px;
+  font-weight: bold;
+}
+.styleLarge {
+  font-size: 14px;
+  font-weight: bold;
+}
+.styleLColored {
+  font-size: 14px;
+  font-weight: bold;
+  color: #666666;
+}
+
+.formatTable td {
+  padding-top: 5px;
+}
+
+.queryTable td {
+  padding-top: 5px;
+}
+
+.allResultsTable td {
+  white-space: nowrap;
+  padding-top: 5px;
+}
+
+.fwdLinkTable td {
+  white-space: nowrap;
+  padding-top: 5px;
+}
+
+.resultTable {
+  margin-top: 5px;
+  border-spacing: 0;
+  border-collapse: collapse;
+  margin-bottom: 10px;
+}
+
+.resultTable th {
+  background-color: #CCCCCC;
+  border: solid #000 1px;
+  font-weight: bold;
+  padding-left: 5px;
+  padding-right: 5px;
+}
+
+.rtd {
+  white-space: nowrap;
+  padding: 5px;
+  border: solid #000 1px;
+}
+
+.borderLess {
+  white-space: nowrap;
+  padding: 0px;
+  font-weight: bold;
+  border: blank;
+}

Added: trunk/src/jar/webquery/resources/template.html
===================================================================
--- trunk/src/jar/webquery/resources/template.html	                        (rev 0)
+++ trunk/src/jar/webquery/resources/template.html	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,98 @@
+<!DOCTYPE html PUBLIC "-//W3C//Dtd XHTML 1.0 Transitional//EN" "http://www.w3.org/tr/xhtml1/Dtd/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+@@debug@@
+-->
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  <title>Mulgara Viewer</title>
+  <link href="styles/ui.css" rel="stylesheet" type="text/css" />
+  <script language="JavaScript" type="text/javascript">
+  <!--
+  // Put the selected example query into the query text area. The graph
+  // name is also inserted in to the query.
+  function setQuery() {
+    // Must have a Model URI
+    if (document.QueryForm.ModelURI.value == '') {
+      alert('Please enter a Model URI and then select the query again');
+      document.QueryForm.ModelURI.focus;
+      return;
+    }
+    var index = document.QueryForm.ExampleQuery.selectedIndex;
+    var exampleText = document.QueryForm.ExampleQuery[index].value;
+    var graphURI= document.QueryForm.ModelURI.value;
+    // Add the graph name
+    var queryString = exampleText.replace(/-graph-/gi, graphURI);
+    document.QueryForm.QueryText.value = queryString;
+  }
+
+  // Validate the form and submit it if all is OK
+  function validateAndSubmit() {
+    // Is there a query?
+    if (document.QueryForm.QueryText.value == null ||
+        document.QueryForm.QueryText.value == "") {
+      alert('Please enter a query');
+      document.QueryForm.QueryText.focus();
+      return false;
+    } else {
+      // Submit the form
+      document.QueryForm.submit();
+      return true;
+    }
+  }
+  //-->
+  </script>
+</head>
+<body>
+  <div id="wrapper">
+    <div id="header">
+      <div id="logo">
+        <a href="http://mulgara.org/"><img src="images/logo.jpg" alt="Mulgara - Semantic Store" border="0" /></a>
+      </div>
+    </div>
+    <div id="content">
+      <table class="formatTable" summary="">
+        <tr>
+          <td>
+            <form action="@@execute@@" method="post" name="QueryForm">
+              <table class="queryTable" summary="Enter a query">
+                <tr>
+                  <td><strong>Model URI:</strong> </td>
+                  <td><input name="ModelURI" size="60" type="text" value="rmi://@@hostname@@/@@servername@@#sampledata"> </td>
+                </tr>
+                <tr>
+                  <td><strong>Example Queries:</strong> </td>
+                  <td>
+                    <select name="ExampleQuery" onchange="javascript:setQuery()">
+                      <option selected value="">Select a query..</option>
+                      <option value="create &lt;-graph-&gt;;">Step 1. Create a graph</option>
+                      <option value="load &lt;@@jarurl@@!/data/w3c-news.rss&gt; into &lt;-graph-&gt;;">Step 2. Load RSS data into the graph</option>
+                      <option value="select $subject $predicate $object from &lt;-graph-&gt; where $subject $predicate $object;">Step 3. List everything in the graph</option>
+                      <option value="select $title $link $description from &lt;-graph-&gt; where $article &lt;http://purl.org/rss/1.0/title&gt; $title and $article &lt;http://purl.org/rss/1.0/link&gt; $link and $article &lt;http://purl.org/rss/1.0/description&gt; $description;">Step 4. List the news articles</option>
+                      <option value="create &lt;rmi://@@hostname@@/@@servername@@#rdfs&gt;;">Step 5. Create a standalone RDF schema graph</option>
+                      <option value="load &lt;@@jarurl@@!/data/rss-1.0.rdfs&gt; into &lt;rmi://@@hostname@@/@@servername@@#rdfs&gt;; load &lt;jar:file:/Users/pag/debug/mulgara-2.0.0.jar!/data/dc.rdfs&gt; into &lt;rmi://@@hostname@@/@@servername@@#rdfs&gt;; load &lt;@@jarurl@@!/data/rdfs.rdfs&gt; into &lt;rmi://@@hostname@@/@@servername@@#rdfs&gt;;">Step 6. Load in the referenced schemas</option>
+                      <option value="select $rdfSubject $rdfProperty $value from &lt;-graph-&gt; or &lt;rmi://@@hostname@@/@@servername@@#rdfs&gt; where $rdfSubject $rdfProperty $value and $rdfProperty &lt;http://www.w3.org/1999/02/22-rdf-syntax-ns#type&gt; &lt;http://www.w3.org/1999/02/22-rdf-syntax-ns#Property&gt; and $rdfProperty &lt;http://www.w3.org/2000/01/rdf-schema#isDefinedBy&gt; &lt;http://purl.org/rss/1.0/&gt;;">Step 7. List the RSS properties using both graphs</option>
+                      <option value="drop &lt;-graph-&gt;; drop &lt;rmi://@@hostname@@/@@servername@@#rdfs&gt;;">Step 8. Drop the graphs</option>
+                    </select>
+                  </td>
+                </tr>
+                <tr>
+                  <td><strong>Query Text:</strong> </td>
+                  <td><textarea cols="80" name="QueryText" rows="10"></textarea> </td>
+                </tr>
+                <tr>
+                  <td>&nbsp;</td>
+                  <td align="left">
+                    <input type="button" value="Submit Query" onclick="validateAndSubmit()">
+                    <input type="button" value="Clear Query" onclick="javascript:document.QueryForm.QueryText.value='';">
+                  </td>
+                </tr>
+              </table>
+            </form>
+          </td>
+        </tr>
+      </table>
+    </div>
+  </div>
+</body>
+</html>

Added: trunk/src/jar/webquery/resources/template_head.html
===================================================================
--- trunk/src/jar/webquery/resources/template_head.html	                        (rev 0)
+++ trunk/src/jar/webquery/resources/template_head.html	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,96 @@
+<!DOCTYPE html PUBLIC "-//W3C//Dtd XHTML 1.0 Transitional//EN" "http://www.w3.org/tr/xhtml1/Dtd/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+@@debug@@
+-->
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  <title>Mulgara Viewer</title>
+  <link href="styles/ui.css" rel="stylesheet" type="text/css" />
+  <script language="JavaScript" type="text/javascript">
+  <!--
+  // Put the selected example query into the query text area. The graph
+  // name is also inserted in to the query.
+  function setQuery() {
+    // Must have a Model URI
+    if (document.QueryForm.ModelURI.value == '') {
+      alert('Please enter a Model URI and then select the query again');
+      document.QueryForm.ModelURI.focus;
+      return;
+    }
+    var index = document.QueryForm.ExampleQuery.selectedIndex;
+    var exampleText = document.QueryForm.ExampleQuery[index].value;
+    var graphURI= document.QueryForm.ModelURI.value;
+    // Add the graph name
+    var queryString = exampleText.replace(/-graph-/gi, graphURI);
+    document.QueryForm.QueryText.value = queryString;
+  }
+
+  // Validate the form and submit it if all is OK
+  function validateAndSubmit() {
+    // Is there a query?
+    if (document.QueryForm.QueryText.value == null ||
+        document.QueryForm.QueryText.value == "") {
+      alert('Please enter a query');
+      document.QueryForm.QueryText.focus();
+      return false;
+    } else {
+      // Submit the form
+      document.QueryForm.submit();
+      return true;
+    }
+  }
+  //-->
+  </script>
+</head>
+<body>
+  <div id="wrapper">
+    <div id="header">
+      <div id="logo">
+        <a href="http://mulgara.org/"><img src="images/logo.jpg" alt="Mulgara - Semantic Store" border="0" /></a>
+      </div>
+    </div>
+    <div id="content">
+      <table class="formatTable" summary="">
+        <tr>
+          <td>
+            <form action="@@execute@@.html" method="post" name="QueryForm">
+              <table class="queryTable" summary="Enter a query">
+                <tr>
+                  <td><strong>Graph URI:</strong> </td>
+                  <td><input name="ModelURI" size="60" type="text" value="@@graph@@"> </td>
+                </tr>
+                <tr>
+                  <td><strong>Example Queries:</strong> </td>
+                  <td>
+                    <select name="ExampleQuery" onchange="javascript:setQuery()">
+                      <option selected value="">Select a query..</option>
+                      <option value="create &lt;-graph-&gt;;">Step 1. Create a graph</option>
+                      <option value="load &lt;@@jarurl@@!/data/w3c-news.rss&gt; into &lt;-graph-&gt;;">Step 2. Load RSS data into the graph</option>
+                      <option value="select $subject $predicate $object from &lt;-graph-&gt; where $subject $predicate $object;">Step 3. List everything in the graph</option>
+                      <option value="select $title $link $description from &lt;-graph-&gt; where $article &lt;http://purl.org/rss/1.0/title&gt; $title and $article &lt;http://purl.org/rss/1.0/link&gt; $link and $article &lt;http://purl.org/rss/1.0/description&gt; $description;">Step 4. List the news articles</option>
+                      <option value="create &lt;rmi://@@hostname@@/@@servername@@#rdfs&gt;;">Step 5. Create a standalone RDF schema graph</option>
+                      <option value="load &lt;@@jarurl@@!/data/rss-1.0.rdfs&gt; into &lt;rmi://@@hostname@@/@@servername@@#rdfs&gt;; load &lt;jar:file:/Users/pag/debug/mulgara-2.0.0.jar!/data/dc.rdfs&gt; into &lt;rmi://@@hostname@@/@@servername@@#rdfs&gt;; load &lt;@@jarurl@@!/data/rdfs.rdfs&gt; into &lt;rmi://@@hostname@@/@@servername@@#rdfs&gt;;">Step 6. Load in the referenced schemas</option>
+                      <option value="select $rdfSubject $rdfProperty $value from &lt;-graph-&gt; or &lt;rmi://@@hostname@@/@@servername@@#rdfs&gt; where $rdfSubject $rdfProperty $value and $rdfProperty &lt;http://www.w3.org/1999/02/22-rdf-syntax-ns#type&gt; &lt;http://www.w3.org/1999/02/22-rdf-syntax-ns#Property&gt; and $rdfProperty &lt;http://www.w3.org/2000/01/rdf-schema#isDefinedBy&gt; &lt;http://purl.org/rss/1.0/&gt;;">Step 7. List the RSS properties using both graphs</option>
+                      <option value="drop &lt;-graph-&gt;; drop &lt;rmi://@@hostname@@/@@servername@@#rdfs&gt;;">Step 8. Drop the graphs</option>
+                    </select>
+                  </td>
+                </tr>
+                <tr>
+                  <td><strong>Query Text:</strong> </td>
+                  <td><textarea cols="80" name="QueryText" rows="10"></textarea> </td>
+                </tr>
+                <tr>
+                  <td>&nbsp;</td>
+                  <td align="left">
+                    <input type="button" value="Submit Query" onclick="validateAndSubmit()">
+                    <input type="button" value="Clear Query" onclick="javascript:document.QueryForm.QueryText.value='';">
+                  </td>
+                </tr>
+              </table>
+            </form>
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <table class="allResultsTable" border="0" cellpadding="5" width="100%">
\ No newline at end of file

Added: trunk/src/jar/webquery/resources/template_more_tail.html
===================================================================
--- trunk/src/jar/webquery/resources/template_more_tail.html	                        (rev 0)
+++ trunk/src/jar/webquery/resources/template_more_tail.html	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,23 @@
+            </table>
+          </td>
+        </tr>
+<!--
+      //               scrollHREF = "ExecuteQuery.event?" + URLEncoder.encode(WebUIKeys.RESULT_INDEX + "=" + resultIndex, "UTF-8");
+-->
+        <tr id="ResultScrollRow">
+          <td colspan="2">
+            <table class="fwdLinkTable">
+              <tr>
+                <td>
+                  <a href="ExecuteQuery" id="ResultForwardLink" title="Forward to next page of results">Next page &gt;</a> &nbsp;
+                </td>
+              </tr>
+            </table>
+          </td>
+        </tr>
+
+      </table>
+    </div>
+  </div>
+</body>
+</html>
\ No newline at end of file

Added: trunk/src/jar/webquery/resources/template_tail.html
===================================================================
--- trunk/src/jar/webquery/resources/template_tail.html	                        (rev 0)
+++ trunk/src/jar/webquery/resources/template_tail.html	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,8 @@
+            </table>
+          </td>
+        </tr>
+      </table>
+    </div>
+  </div>
+</body>
+</html>
\ No newline at end of file

Added: trunk/src/jar/webquery/webquery-build.properties
===================================================================
--- trunk/src/jar/webquery/webquery-build.properties	                        (rev 0)
+++ trunk/src/jar/webquery/webquery-build.properties	2008-08-10 02:06:08 UTC (rev 1128)
@@ -0,0 +1,15 @@
+#
+# Properties used by the webquery module
+#
+
+# Module Name
+webquery.name          = webquery
+
+# General module properties
+webquery.conf.dir      = ${conf.dir}
+webquery.src.dir       = ${jar.src.dir}/webquery
+webquery.obj.dir       = ${jar.obj.dir}/webquery
+webquery.dist.dir      = ${bin.dir}
+webquery.test.dir      = ${test.dir}/webquery
+webquery.jxtest.dir    = ${jxtest.dir}/webquery
+webquery.jar           = webquery-base-${mulgara-version}.jar




More information about the Mulgara-svn mailing list