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