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 }