001 /* 002 * Copyright (c) 2007 Henri Sivonen 003 * Copyright (c) 2008 Mozilla Foundation 004 * 005 * Permission is hereby granted, free of charge, to any person obtaining a 006 * copy of this software and associated documentation files (the "Software"), 007 * to deal in the Software without restriction, including without limitation 008 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 009 * and/or sell copies of the Software, and to permit persons to whom the 010 * Software is furnished to do so, subject to the following conditions: 011 * 012 * The above copyright notice and this permission notice shall be included in 013 * all copies or substantial portions of the Software. 014 * 015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 016 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 017 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 018 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 019 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 020 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 021 * DEALINGS IN THE SOFTWARE. 022 */ 023 024 package nu.validator.saxtree; 025 026 import org.xml.sax.Attributes; 027 import org.xml.sax.ContentHandler; 028 import org.xml.sax.Locator; 029 import org.xml.sax.SAXException; 030 import org.xml.sax.ext.LexicalHandler; 031 032 /** 033 * A tree visitor that replays a tree as SAX events. 034 * @version $Id$ 035 * @author hsivonen 036 */ 037 public final class TreeParser implements Locator { 038 039 /** 040 * The content handler. 041 */ 042 private final ContentHandler contentHandler; 043 044 /** 045 * The lexical handler. 046 */ 047 private final LexicalHandler lexicalHandler; 048 049 /** 050 * The current locator. 051 */ 052 private Locator locatorDelegate; 053 054 /** 055 * The constructor. 056 * 057 * @param contentHandler 058 * must not be <code>null</code> 059 * @param lexicalHandler 060 * may be <code>null</code> 061 */ 062 public TreeParser(final ContentHandler contentHandler, 063 final LexicalHandler lexicalHandler) { 064 if (contentHandler == null) { 065 throw new IllegalArgumentException("contentHandler was null."); 066 } 067 this.contentHandler = contentHandler; 068 if (lexicalHandler == null) { 069 this.lexicalHandler = new NullLexicalHandler(); 070 } else { 071 this.lexicalHandler = lexicalHandler; 072 } 073 } 074 075 /** 076 * Causes SAX events for the tree rooted at the argument to be emitted. 077 * <code>startDocument()</code> and <code>endDocument()</code> are only 078 * emitted for a <code>Document</code> node. 079 * 080 * @param node 081 * the root 082 * @throws SAXException 083 */ 084 public void parse(Node node) throws SAXException { 085 contentHandler.setDocumentLocator(this); 086 Node current = node; 087 Node next; 088 for (;;) { 089 current.visit(this); 090 if ((next = current.getFirstChild()) != null) { 091 current = next; 092 continue; 093 } 094 for (;;) { 095 current.revisit(this); 096 if (current == node) { 097 return; 098 } 099 if ((next = current.getNextSibling()) != null) { 100 current = next; 101 break; 102 } 103 current = current.getParentNode(); 104 } 105 } 106 } 107 108 /** 109 * @see org.xml.sax.ContentHandler#characters(char[], int, int) 110 */ 111 void characters(char[] ch, int start, int length, Locator locator) 112 throws SAXException { 113 this.locatorDelegate = locator; 114 contentHandler.characters(ch, start, length); 115 } 116 117 /** 118 * @see org.xml.sax.ContentHandler#endDocument() 119 */ 120 void endDocument(Locator locator) throws SAXException { 121 this.locatorDelegate = locator; 122 contentHandler.endDocument(); 123 } 124 125 /** 126 * @see org.xml.sax.ContentHandler#endElement(java.lang.String, 127 * java.lang.String, java.lang.String) 128 */ 129 void endElement(String uri, String localName, String qName, Locator locator) 130 throws SAXException { 131 this.locatorDelegate = locator; 132 contentHandler.endElement(uri, localName, qName); 133 } 134 135 /** 136 * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String) 137 */ 138 void endPrefixMapping(String prefix, Locator locator) throws SAXException { 139 this.locatorDelegate = locator; 140 contentHandler.endPrefixMapping(prefix); 141 } 142 143 /** 144 * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int) 145 */ 146 void ignorableWhitespace(char[] ch, int start, int length, Locator locator) 147 throws SAXException { 148 this.locatorDelegate = locator; 149 contentHandler.ignorableWhitespace(ch, start, length); 150 } 151 152 /** 153 * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, 154 * java.lang.String) 155 */ 156 void processingInstruction(String target, String data, Locator locator) 157 throws SAXException { 158 this.locatorDelegate = locator; 159 contentHandler.processingInstruction(target, data); 160 } 161 162 /** 163 * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String) 164 */ 165 void skippedEntity(String name, Locator locator) throws SAXException { 166 this.locatorDelegate = locator; 167 contentHandler.skippedEntity(name); 168 } 169 170 /** 171 * @see org.xml.sax.ContentHandler#startDocument() 172 */ 173 void startDocument(Locator locator) throws SAXException { 174 this.locatorDelegate = locator; 175 contentHandler.startDocument(); 176 } 177 178 /** 179 * @see org.xml.sax.ContentHandler#startElement(java.lang.String, 180 * java.lang.String, java.lang.String, org.xml.sax.Attributes) 181 */ 182 void startElement(String uri, String localName, String qName, 183 Attributes atts, Locator locator) throws SAXException { 184 this.locatorDelegate = locator; 185 contentHandler.startElement(uri, localName, qName, atts); 186 } 187 188 /** 189 * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, 190 * java.lang.String) 191 */ 192 void startPrefixMapping(String prefix, String uri, Locator locator) 193 throws SAXException { 194 this.locatorDelegate = locator; 195 contentHandler.startPrefixMapping(prefix, uri); 196 } 197 198 /** 199 * @see org.xml.sax.ext.LexicalHandler#comment(char[], int, int) 200 */ 201 void comment(char[] ch, int start, int length, Locator locator) 202 throws SAXException { 203 this.locatorDelegate = locator; 204 lexicalHandler.comment(ch, start, length); 205 } 206 207 /** 208 * @see org.xml.sax.ext.LexicalHandler#endCDATA() 209 */ 210 void endCDATA(Locator locator) throws SAXException { 211 this.locatorDelegate = locator; 212 lexicalHandler.endCDATA(); 213 } 214 215 /** 216 * @see org.xml.sax.ext.LexicalHandler#endDTD() 217 */ 218 void endDTD(Locator locator) throws SAXException { 219 this.locatorDelegate = locator; 220 lexicalHandler.endDTD(); 221 } 222 223 /** 224 * @see org.xml.sax.ext.LexicalHandler#endEntity(java.lang.String) 225 */ 226 void endEntity(String name, Locator locator) throws SAXException { 227 this.locatorDelegate = locator; 228 lexicalHandler.endEntity(name); 229 } 230 231 /** 232 * @see org.xml.sax.ext.LexicalHandler#startCDATA() 233 */ 234 void startCDATA(Locator locator) throws SAXException { 235 this.locatorDelegate = locator; 236 lexicalHandler.startCDATA(); 237 } 238 239 /** 240 * @see org.xml.sax.ext.LexicalHandler#startDTD(java.lang.String, 241 * java.lang.String, java.lang.String) 242 */ 243 void startDTD(String name, String publicId, String systemId, Locator locator) 244 throws SAXException { 245 this.locatorDelegate = locator; 246 lexicalHandler.startDTD(name, publicId, systemId); 247 } 248 249 /** 250 * @see org.xml.sax.ext.LexicalHandler#startEntity(java.lang.String) 251 */ 252 void startEntity(String name, Locator locator) throws SAXException { 253 this.locatorDelegate = locator; 254 lexicalHandler.startEntity(name); 255 } 256 257 /** 258 * @see org.xml.sax.Locator#getColumnNumber() 259 */ 260 public int getColumnNumber() { 261 if (locatorDelegate == null) { 262 return -1; 263 } else { 264 return locatorDelegate.getColumnNumber(); 265 } 266 } 267 268 /** 269 * @see org.xml.sax.Locator#getLineNumber() 270 */ 271 public int getLineNumber() { 272 if (locatorDelegate == null) { 273 return -1; 274 } else { 275 return locatorDelegate.getLineNumber(); 276 } 277 } 278 279 /** 280 * @see org.xml.sax.Locator#getPublicId() 281 */ 282 public String getPublicId() { 283 if (locatorDelegate == null) { 284 return null; 285 } else { 286 287 return locatorDelegate.getPublicId(); 288 } 289 } 290 291 /** 292 * @see org.xml.sax.Locator#getSystemId() 293 */ 294 public String getSystemId() { 295 if (locatorDelegate == null) { 296 return null; 297 } else { 298 return locatorDelegate.getSystemId(); 299 } 300 } 301 }