+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></[element name]></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