SAXTreeViewer requires java 1.5 or later.
[vamsas.git] / src / uk / ac / vamsas / test / document / SAXTreeViewer.java
diff --git a/src/uk/ac/vamsas/test/document/SAXTreeViewer.java b/src/uk/ac/vamsas/test/document/SAXTreeViewer.java
new file mode 100644 (file)
index 0000000..59c2313
--- /dev/null
@@ -0,0 +1,601 @@
+package uk.ac.vamsas.test.document;\r
+\r
+\r
+/*-- \r
+\r
+ Copyright (C) 2001 Brett McLaughlin.\r
+ All rights reserved.\r
\r
+ Redistribution and use in source and binary forms, with or without\r
+ modification, are permitted provided that the following conditions\r
+ are met:\r
\r
+ 1. Redistributions of source code must retain the above copyright\r
+    notice, this list of conditions, and the following disclaimer.\r
\r
+ 2. Redistributions in binary form must reproduce the above copyright\r
+    notice, this list of conditions, and the disclaimer that follows \r
+    these conditions in the documentation and/or other materials \r
+    provided with the distribution.\r
+\r
+ 3. The name "Java and XML" must not be used to endorse or promote products\r
+    derived from this software without prior written permission.  For\r
+    written permission, please contact brett@newInstance.com.\r
\r
+ In addition, we request (but do not require) that you include in the \r
+ end-user documentation provided with the redistribution and/or in the \r
+ software itself an acknowledgement equivalent to the following:\r
+     "This product includes software developed for the\r
+      'Java and XML' book, by Brett McLaughlin (O'Reilly & Associates)."\r
+\r
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED\r
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\r
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
+ DISCLAIMED.  IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT\r
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\r
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\r
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\r
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+ SUCH DAMAGE.\r
+\r
+ */\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.util.HashMap;\r
+import java.util.Iterator;\r
+import java.util.Map;\r
+import org.xml.sax.Attributes;\r
+import org.xml.sax.ContentHandler;\r
+import org.xml.sax.ErrorHandler;\r
+import org.xml.sax.InputSource;\r
+import org.xml.sax.Locator;\r
+import org.xml.sax.SAXException;\r
+import org.xml.sax.SAXParseException;\r
+import org.xml.sax.XMLReader;\r
+import org.xml.sax.helpers.XMLReaderFactory;\r
+\r
+import uk.ac.vamsas.client.simpleclient.FileWatcher;\r
+import uk.ac.vamsas.client.simpleclient.Lock;\r
+import uk.ac.vamsas.client.simpleclient.SimpleDocument;\r
+import uk.ac.vamsas.client.simpleclient.VamsasArchiveReader;\r
+\r
+// This is an XML book - no need for explicit Swing imports\r
+import java.awt.*;\r
+import javax.swing.*;\r
+import javax.swing.tree.*;\r
+\r
+/**\r
+ * <b><code>SAXTreeViewer</code></b> uses Swing to graphically\r
+ *   display an XML document.\r
+ */\r
+public class SAXTreeViewer extends JFrame {\r
+  /** Default parser to use */\r
+private String vendorParserClass = \r
+    "org.apache.xerces.parsers.SAXParser";\r
+\r
+/** The base tree to render */\r
+private JTree jTree;\r
+\r
+/** Tree model to use */\r
+DefaultTreeModel defaultTreeModel;\r
+\r
+/**\r
+ * <p> This initializes the needed Swing settings. </p>\r
+ */\r
+public SAXTreeViewer() {\r
+    // Handle Swing setup\r
+    super("SAX Tree Viewer");\r
+    setSize(600, 450);\r
+}\r
+\r
+/**\r
+ * <p> This will construct the tree using Swing. </p>\r
+ *\r
+ * @param filename <code>String</code> path to XML document.\r
+ */\r
+public void init(String xmlURI) throws IOException, SAXException {\r
+  init(xmlURI, null);\r
+}\r
+/**\r
+ * <p> This will construct the tree using Swing. </p>\r
+ *\r
+ * @param filename <code>String</code> apparent path to XML document.\r
+ * @param inputSource <code>InputSource</code> content of XML document\r
+ */\r
+  public void init(String xmlURI, InputSource inputSource) throws IOException, SAXException {\r
+    \r
+    DefaultMutableTreeNode base = \r
+        new DefaultMutableTreeNode("XML Document: " + \r
+            xmlURI);\r
+    \r
+    // Build the tree model\r
+    defaultTreeModel = new DefaultTreeModel(base);\r
+    jTree = new JTree(defaultTreeModel);\r
+\r
+    // Construct the tree hierarchy\r
+    if (inputSource==null) {\r
+      buildTree(defaultTreeModel, base, xmlURI);\r
+    } else {\r
+      buildTree(defaultTreeModel, base, xmlURI,inputSource);\r
+    }\r
+    // Display the results\r
+    getContentPane().add(new JScrollPane(jTree), \r
+        BorderLayout.CENTER);\r
+}\r
+\r
+/**\r
+ * <p>This handles building the Swing UI tree.</p>\r
+ *\r
+ * @param treeModel Swing component to build upon.\r
+ * @param base tree node to build on.\r
+ * @param xmlURI URI to build XML document from.\r
+ * @throws <code>IOException</code> - when reading the XML URI fails.\r
+ * @throws <code>SAXException</code> - when errors in parsing occur.\r
+ */\r
+public void buildTree(DefaultTreeModel treeModel, \r
+                      DefaultMutableTreeNode base, String xmlURI) \r
+    throws IOException, SAXException {\r
+  // Parse\r
+  InputSource inputSource = \r
+      new InputSource(xmlURI);\r
+  buildTree(treeModel,base,xmlURI,inputSource);\r
+}\r
+/**\r
+ * <p>This handles building the Swing UI tree.</p>\r
+ *\r
+ * @param treeModel Swing component to build upon.\r
+ * @param base tree node to build on.\r
+ * @param xmlURI apparent URI to build XML document from.\r
+ * @param inputSource the xml datasource to get the content from\r
+ * @throws SAXException \r
+ * @throws IOException \r
+ * @throws <code>IOException</code> - when reading the XML URI fails.\r
+ * @throws <code>SAXException</code> - when errors in parsing occur.\r
+ */\r
+public void buildTree(DefaultTreeModel treeModel, \r
+    DefaultMutableTreeNode base, String xmlURI, InputSource inputSource) throws IOException, SAXException {\r
+\r
+    // Create instances needed for parsing\r
+    XMLReader reader = \r
+        XMLReaderFactory.createXMLReader(vendorParserClass);\r
+    ContentHandler jTreeContentHandler = \r
+        new JTreeContentHandler(treeModel, base);\r
+    ErrorHandler jTreeErrorHandler = new JTreeErrorHandler();\r
+\r
+    // Register content handler\r
+    reader.setContentHandler(jTreeContentHandler);\r
+\r
+    // Register error handler\r
+    reader.setErrorHandler(jTreeErrorHandler);\r
+\r
+    reader.parse(inputSource);\r
+}\r
+private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(SAXTreeViewer.class);\r
+\r
+/**\r
+ * <p> Static entry point for running the viewer. </p>\r
+ */\r
+public static void main(String[] args) {\r
+    try {\r
+      File archive = new File(args[0]);\r
+      // start watching a vamsas document archive\r
+      // watch\r
+      log.info("Endlessly Watching file " + archive);\r
+      /*\r
+       * if (!archive.exists()) archive.createNewFile();\r
+       */// watch the new file... - taken straight from ClientsFileTest\r
+      FileWatcher w = new FileWatcher(archive);\r
+      SAXTreeViewer currentview = null;\r
+      boolean first=true;\r
+      while (true) {\r
+        // get watcher's lock to ensure state change is fixed for\r
+        // retrieval\r
+        Lock chlock = w.getChangedState();\r
+        if (first || chlock != null) {\r
+          log.info("Got lock on "\r
+              + archive\r
+              + (archive.exists() ? " exists l=" + archive.length()\r
+                  : "(non existant)"));\r
+          first = false;\r
+          if (archive.length() > 0) {\r
+            VamsasArchiveReader vreader = new VamsasArchiveReader(archive);\r
+            SimpleDocument sdoc = new SimpleDocument(\r
+                "testing vamsas watcher");\r
+            try {\r
+              // pass the archive XML content to the xml viewer.\r
+              SAXTreeViewer newview = new SAXTreeViewer();\r
+              newview.init(archive.toURI().toString(), new org.xml.sax.InputSource(vreader.getVamsasDocumentStream()));\r
+              if (currentview != null)\r
+              {\r
+                newview.setBounds(currentview.getBounds());\r
+                // somehow copy over expanded state for existing objects and scroll state.\r
+                // could also highlight new / modified nodes.\r
+                newview.setVisible(true);\r
+                currentview.setVisible(false);\r
+                currentview.dispose();\r
+              } else {\r
+                newview.setVisible(true);\r
+              }\r
+              currentview = newview;\r
+              \r
+              /* VamsasDocument d = sdoc.getVamsasDocument(vreader);\r
+              if (d != null) {\r
+                ArchiveReports.reportDocument(d, vreader, false,\r
+                    System.out);\r
+              }*/\r
+              System.out\r
+                  .println("Update at "\r
+                      + System.currentTimeMillis()\r
+                      + "\n\n********************************************************\n");\r
+            } catch (Exception e) {\r
+              log.error("Unmarshalling failed.", e);\r
+            }\r
+            vreader.close();\r
+            w.setState();\r
+          } \r
+        }\r
+        Thread.sleep(2000);\r
+      }\r
+    } catch (Exception e) {\r
+      log.info("Going away now.",e);\r
+    }\r
+}\r
+}\r
+\r
+/**\r
+* <b><code>JTreeContentHandler</code></b> implements the SAX\r
+*   <code>ContentHandler</code> interface and defines callback\r
+*   behavior for the SAX callbacks associated with an XML\r
+*   document's content, bulding up JTree nodes.\r
+*/\r
+class JTreeContentHandler implements ContentHandler {\r
+\r
+/** Hold onto the locator for location information */\r
+private Locator locator;\r
+\r
+/** Store URI to prefix mappings */\r
+private Map namespaceMappings;\r
+\r
+/** Tree Model to add nodes to */\r
+private DefaultTreeModel treeModel;\r
+\r
+/** Current node to add sub-nodes to */\r
+private DefaultMutableTreeNode current;\r
+\r
+/**\r
+ * <p> Set up for working with the JTree. </p>\r
+ *\r
+ * @param treeModel tree to add nodes to.\r
+ * @param base node to start adding sub-nodes to.\r
+ */\r
+public JTreeContentHandler(DefaultTreeModel treeModel, \r
+                           DefaultMutableTreeNode base) {\r
+    this.treeModel = treeModel;\r
+    this.current = base;\r
+    this.namespaceMappings = new HashMap();\r
+}\r
+\r
+/**\r
+ * <p>\r
+ *  Provide reference to <code>Locator</code> which provides\r
+ *    information about where in a document callbacks occur.\r
+ * </p>\r
+ *\r
+ * @param locator <code>Locator</code> object tied to callback\r
+ *        process\r
+ */\r
+public void setDocumentLocator(Locator locator) {\r
+    // Save this for later use\r
+    this.locator = locator;\r
+}\r
+\r
+/**\r
+ * <p>\r
+ *  This indicates the start of a Document parse-this precedes\r
+ *    all callbacks in all SAX Handlers with the sole exception\r
+ *    of <code>{@link #setDocumentLocator}</code>.\r
+ * </p>\r
+ *\r
+ * @throws <code>SAXException</code> when things go wrong\r
+ */\r
+public void startDocument() throws SAXException {\r
+    // No visual events occur here\r
+}\r
+\r
+/**\r
+ * <p>\r
+ *  This indicates the end of a Document parse-this occurs after\r
+ *    all callbacks in all SAX Handlers.</code>.\r
+ * </p>\r
+ *\r
+ * @throws <code>SAXException</code> when things go wrong\r
+ */\r
+public void endDocument() throws SAXException {\r
+    // No visual events occur here\r
+}\r
+\r
+/**\r
+ * <p>\r
+ *   This indicates that a processing instruction (other than\r
+ *     the XML declaration) has been encountered.\r
+ * </p>\r
+ *\r
+ * @param target <code>String</code> target of PI\r
+ * @param data <code>String</code containing all data sent to the PI.\r
+ *               This typically looks like one or more attribute value\r
+ *               pairs.\r
+ * @throws <code>SAXException</code> when things go wrong\r
+ */\r
+public void processingInstruction(String target, String data)\r
+    throws SAXException {\r
+\r
+    DefaultMutableTreeNode pi = \r
+        new DefaultMutableTreeNode("PI (target = '" + target +\r
+                                   "', data = '" + data + "')");\r
+    current.add(pi);\r
+}\r
+\r
+/**\r
+ * <p>\r
+ *   This indicates the beginning of an XML Namespace prefix\r
+ *     mapping. Although this typically occurs within the root element\r
+ *     of an XML document, it can occur at any point within the\r
+ *     document. Note that a prefix mapping on an element triggers\r
+ *     this callback <i>before</i> the callback for the actual element\r
+ *     itself (<code>{@link #startElement}</code>) occurs.\r
+ * </p>\r
+ *\r
+ * @param prefix <code>String</code> prefix used for the namespace\r
+ *                being reported\r
+ * @param uri <code>String</code> URI for the namespace\r
+ *               being reported\r
+ * @throws <code>SAXException</code> when things go wrong\r
+ */\r
+public void startPrefixMapping(String prefix, String uri) {\r
+    // No visual events occur here.\r
+    namespaceMappings.put(uri, prefix);\r
+}\r
+\r
+/**\r
+ * <p>\r
+ *   This indicates the end of a prefix mapping, when the namespace\r
+ *     reported in a <code>{@link #startPrefixMapping}</code> callback\r
+ *     is no longer available.\r
+ * </p>\r
+ *\r
+ * @param prefix <code>String</code> of namespace being reported\r
+ * @throws <code>SAXException</code> when things go wrong\r
+ */\r
+public void endPrefixMapping(String prefix) {\r
+    // No visual events occur here.\r
+    for (Iterator i = namespaceMappings.keySet().iterator(); \r
+         i.hasNext(); ) {\r
+\r
+        String uri = (String)i.next();\r
+        String thisPrefix = (String)namespaceMappings.get(uri);\r
+        if (prefix.equals(thisPrefix)) {\r
+            namespaceMappings.remove(uri);\r
+            break;\r
+        }\r
+    }\r
+}\r
+\r
+/**\r
+ * <p>\r
+ *   This reports the occurrence of an actual element. It includes\r
+ *     the element's attributes, with the exception of XML vocabulary\r
+ *     specific attributes, such as\r
+ *     <code>xmlns:[namespace prefix]</code> and\r
+ *     <code>xsi:schemaLocation</code>.\r
+ * </p>\r
+ *\r
+ * @param namespaceURI <code>String</code> namespace URI this element\r
+ *               is associated with, or an empty <code>String</code>\r
+ * @param localName <code>String</code> name of element (with no\r
+ *               namespace prefix, if one is present)\r
+ * @param qName <code>String</code> XML 1.0 version of element name:\r
+ *                [namespace prefix]:[localName]\r
+ * @param atts <code>Attributes</code> list for this element\r
+ * @throws <code>SAXException</code> when things go wrong\r
+ */\r
+public void startElement(String namespaceURI, String localName,\r
+                         String qName, Attributes atts)\r
+    throws SAXException {\r
+\r
+    DefaultMutableTreeNode element = \r
+        new DefaultMutableTreeNode("Element: " + localName);\r
+    current.add(element);\r
+    current = element;\r
+\r
+    // Determine namespace\r
+    if (namespaceURI.length() > 0) {\r
+        String prefix = \r
+            (String)namespaceMappings.get(namespaceURI);\r
+        if (prefix.equals("")) {\r
+            prefix = "[None]";\r
+        }\r
+        DefaultMutableTreeNode namespace =\r
+            new DefaultMutableTreeNode("Namespace: prefix = '" +\r
+                prefix + "', URI = '" + namespaceURI + "'");\r
+        current.add(namespace);\r
+    }\r
+\r
+    // Process attributes\r
+    for (int i=0; i<atts.getLength(); i++) {\r
+        DefaultMutableTreeNode attribute =\r
+            new DefaultMutableTreeNode("Attribute (name = '" +\r
+                                       atts.getLocalName(i) + \r
+                                       "', value = '" +\r
+                                       atts.getValue(i) + "')");\r
+        String attURI = atts.getURI(i);\r
+        if (attURI.length() > 0) {\r
+            String attPrefix = \r
+                (String)namespaceMappings.get(namespaceURI);\r
+            if (attPrefix.equals("")) {\r
+                attPrefix = "[None]";\r
+            }\r
+            DefaultMutableTreeNode attNamespace =\r
+                new DefaultMutableTreeNode("Namespace: prefix = '" +\r
+                    attPrefix + "', URI = '" + attURI + "'");\r
+            attribute.add(attNamespace);            \r
+        }\r
+        current.add(attribute);\r
+    }\r
+}\r
+\r
+/**\r
+ * <p>\r
+ *   Indicates the end of an element\r
+ *     (<code>&lt;/[element name]&gt;</code>) is reached. Note that\r
+ *     the parser does not distinguish between empty\r
+ *     elements and non-empty elements, so this occurs uniformly.\r
+ * </p>\r
+ *\r
+ * @param namespaceURI <code>String</code> URI of namespace this\r
+ *                element is associated with\r
+ * @param localName <code>String</code> name of element without prefix\r
+ * @param qName <code>String</code> name of element in XML 1.0 form\r
+ * @throws <code>SAXException</code> when things go wrong\r
+ */\r
+public void endElement(String namespaceURI, String localName,\r
+                       String qName)\r
+    throws SAXException {\r
+\r
+    // Walk back up the tree\r
+    current = (DefaultMutableTreeNode)current.getParent();\r
+}\r
+\r
+/**\r
+ * <p>\r
+ *   This reports character data (within an element).\r
+ * </p>\r
+ *\r
+ * @param ch <code>char[]</code> character array with character data\r
+ * @param start <code>int</code> index in array where data starts.\r
+ * @param length <code>int</code> index in array where data ends.\r
+ * @throws <code>SAXException</code> when things go wrong\r
+ */\r
+public void characters(char[] ch, int start, int length)\r
+    throws SAXException {\r
+\r
+    String s = new String(ch, start, length);\r
+    DefaultMutableTreeNode data =\r
+        new DefaultMutableTreeNode("Character Data: '" + s + "'");\r
+    current.add(data);\r
+}\r
+\r
+/**\r
+ * <p>\r
+ * This reports whitespace that can be ignored in the\r
+ * originating document. This is typically invoked only when\r
+ * validation is ocurring in the parsing process.\r
+ * </p>\r
+ *\r
+ * @param ch <code>char[]</code> character array with character data\r
+ * @param start <code>int</code> index in array where data starts.\r
+ * @param end <code>int</code> index in array where data ends.\r
+ * @throws <code>SAXException</code> when things go wrong\r
+ */\r
+public void ignorableWhitespace(char[] ch, int start, int length)\r
+    throws SAXException {\r
+    \r
+    // This is ignorable, so don't display it\r
+}\r
+\r
+/**\r
+ * <p>\r
+ *   This reports an entity that is skipped by the parser. This\r
+ *     should only occur for non-validating parsers, and then is still\r
+ *     implementation-dependent behavior.\r
+ * </p>\r
+ *\r
+ * @param name <code>String</code> name of entity being skipped\r
+ * @throws <code>SAXException</code> when things go wrong\r
+ */\r
+public void skippedEntity(String name) throws SAXException {\r
+    DefaultMutableTreeNode skipped =\r
+        new DefaultMutableTreeNode("Skipped Entity: '" + name + "'");\r
+    current.add(skipped);\r
+}\r
+}\r
+\r
+/**\r
+* <b><code>JTreeErrorHandler</code></b> implements the SAX\r
+*   <code>ErrorHandler</code> interface and defines callback\r
+*   behavior for the SAX callbacks associated with an XML\r
+*   document's warnings and errors.\r
+*/\r
+class JTreeErrorHandler implements ErrorHandler {\r
+\r
+/**\r
+ * <p>\r
+ * This will report a warning that has occurred; this indicates\r
+ *   that while no XML rules were "broken", something appears\r
+ *   to be incorrect or missing.\r
+ * </p>\r
+ *\r
+ * @param exception <code>SAXParseException</code> that occurred.\r
+ * @throws <code>SAXException</code> when things go wrong \r
+ */\r
+public void warning(SAXParseException exception)\r
+    throws SAXException {\r
+        \r
+    System.out.println("**Parsing Warning**\n" +\r
+                       "  Line:    " + \r
+                          exception.getLineNumber() + "\n" +\r
+                       "  URI:     " + \r
+                          exception.getSystemId() + "\n" +\r
+                       "  Message: " + \r
+                          exception.getMessage());        \r
+    throw new SAXException("Warning encountered");\r
+}\r
+\r
+/**\r
+ * <p>\r
+ * This will report an error that has occurred; this indicates\r
+ *   that a rule was broken, typically in validation, but that\r
+ *   parsing can reasonably continue.\r
+ * </p>\r
+ *\r
+ * @param exception <code>SAXParseException</code> that occurred.\r
+ * @throws <code>SAXException</code> when things go wrong \r
+ */\r
+public void error(SAXParseException exception)\r
+    throws SAXException {\r
+    \r
+    System.out.println("**Parsing Error**\n" +\r
+                       "  Line:    " + \r
+                          exception.getLineNumber() + "\n" +\r
+                       "  URI:     " + \r
+                          exception.getSystemId() + "\n" +\r
+                       "  Message: " + \r
+                          exception.getMessage());\r
+    throw new SAXException("Error encountered");\r
+}\r
+\r
+/**\r
+ * <p>\r
+ * This will report a fatal error that has occurred; this indicates\r
+ *   that a rule has been broken that makes continued parsing either\r
+ *   impossible or an almost certain waste of time.\r
+ * </p>\r
+ *\r
+ * @param exception <code>SAXParseException</code> that occurred.\r
+ * @throws <code>SAXException</code> when things go wrong \r
+ */\r
+public void fatalError(SAXParseException exception)\r
+    throws SAXException {\r
+\r
+    System.out.println("**Parsing Fatal Error**\n" +\r
+                       "  Line:    " + \r
+                          exception.getLineNumber() + "\n" +\r
+                       "  URI:     " + \r
+                          exception.getSystemId() + "\n" +\r
+                       "  Message: " + \r
+                          exception.getMessage());        \r
+    throw new SAXException("Fatal Error encountered");\r
+}\r
+}
\ No newline at end of file