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.htmlparser.sax;
024    
025    import org.xml.sax.Attributes;
026    import org.xml.sax.ContentHandler;
027    import org.xml.sax.SAXException;
028    import org.xml.sax.ext.LexicalHandler;
029    
030    import nu.validator.htmlparser.common.XmlViolationPolicy;
031    import nu.validator.htmlparser.impl.AttributesImpl;
032    import nu.validator.htmlparser.impl.TreeBuilder;
033    
034    class SAXStreamer extends TreeBuilder<Attributes>{
035    
036        private ContentHandler contentHandler;
037        private LexicalHandler lexicalHandler;
038        private int depth;
039        
040        SAXStreamer() {
041            super(XmlViolationPolicy.FATAL, false);
042        }
043        
044        @Override
045        protected void addAttributesToElement(Attributes element, Attributes attributes) throws SAXException {
046            Attributes existingAttrs = element;
047            for (int i = 0; i < attributes.getLength(); i++) {
048                String qName = attributes.getQName(i);
049                if (existingAttrs.getIndex(qName) < 0) {
050                    fatal();
051                }
052            }
053        }
054    
055        @Override
056        protected void appendCharacters(Attributes parent, char[] buf, int start, int length) throws SAXException {
057            contentHandler.characters(buf, start, length);
058        }
059    
060        @Override
061        protected void appendChildrenToNewParent(Attributes oldParent, Attributes newParent) throws SAXException {
062            fatal();
063        }
064    
065        @Override
066        protected void appendComment(Attributes parent, char[] buf, int start, int length) throws SAXException {
067            if (lexicalHandler != null) {
068                lexicalHandler.comment(buf, start, length);
069            }
070        }
071    
072        @Override
073        protected void appendCommentToDocument(char[] buf, int start, int length)
074                throws SAXException {
075            if (lexicalHandler != null) {
076                if (depth == 0) {
077                    lexicalHandler.comment(buf, start, length);
078                } else {
079                    fatal();
080                }
081            }
082        }
083    
084        @Override
085        protected Attributes createElement(String name, Attributes attributes) throws SAXException {
086            return attributes;
087        }
088    
089        @Override
090        protected Attributes createHtmlElementSetAsRoot(Attributes attributes) throws SAXException {
091            return attributes;
092        }
093    
094        @Override
095        protected void detachFromParent(Attributes element) throws SAXException {
096            fatal();
097        }
098    
099        @Override
100        protected void detachFromParentAndAppendToNewParent(Attributes child, Attributes newParent) throws SAXException {
101        }
102    
103        @Override
104        protected boolean hasChildren(Attributes element) throws SAXException {
105            return false;
106        }
107    
108        @Override
109        protected void insertBefore(Attributes child, Attributes sibling, Attributes parent) throws SAXException {
110            fatal();
111        }
112    
113        @Override
114        protected void insertCharactersBefore(char[] buf, int start, int length, Attributes sibling, Attributes parent) throws SAXException {
115            fatal();
116        }
117    
118        @Override
119        protected Attributes parentElementFor(Attributes child) throws SAXException {
120            fatal();
121            throw new RuntimeException("Unreachable");
122        }
123    
124        @Override
125        protected Attributes shallowClone(Attributes element) throws SAXException {
126            return element;
127        }
128        
129        public void setContentHandler(ContentHandler handler) {
130            contentHandler = handler;
131        }
132    
133        public void setLexicalHandler(LexicalHandler handler) {
134            lexicalHandler = handler;
135        }
136    
137        /**
138         * @see nu.validator.htmlparser.impl.TreeBuilder#appendDoctypeToDocument(java.lang.String, java.lang.String, java.lang.String)
139         */
140        @Override
141        protected void appendDoctypeToDocument(String name, String publicIdentifier, String systemIdentifier) throws SAXException {
142            if (lexicalHandler != null) {
143                lexicalHandler.startDTD(name, publicIdentifier, systemIdentifier);
144                lexicalHandler.endDTD();
145            }
146        }
147    
148        /**
149         * @see nu.validator.htmlparser.impl.TreeBuilder#bodyClosed(java.lang.Object)
150         */
151        @Override
152        protected void bodyClosed(Attributes body) throws SAXException {
153            contentHandler.endElement("http://www.w3.org/1999/xhtml", "body", "body");
154            depth--;
155        }
156    
157        /**
158         * @see nu.validator.htmlparser.impl.TreeBuilder#elementPopped(java.lang.String, java.lang.Object)
159         */
160        @Override
161        protected void elementPopped(String name, Attributes node) throws SAXException {
162            contentHandler.endElement("http://www.w3.org/1999/xhtml", name, name);
163            depth--;        
164        }
165    
166        /**
167         * @see nu.validator.htmlparser.impl.TreeBuilder#elementPushed(java.lang.String, java.lang.Object)
168         */
169        @Override
170        protected void elementPushed(String name, Attributes node) throws SAXException {
171            if (depth == 0) {
172                contentHandler.startPrefixMapping("", "http://www.w3.org/1999/xhtml");
173            }
174            contentHandler.startElement("http://www.w3.org/1999/xhtml", name, name, node);
175            depth++;
176        }
177    
178        /**
179         * @see nu.validator.htmlparser.impl.TreeBuilder#end()
180         */
181        @Override
182        protected void end() throws SAXException {
183            contentHandler.endDocument();
184        }
185    
186        /**
187         * @see nu.validator.htmlparser.impl.TreeBuilder#htmlClosed(java.lang.Object)
188         */
189        @Override
190        protected void htmlClosed(Attributes html) throws SAXException {
191            contentHandler.endElement("http://www.w3.org/1999/xhtml", "html", "html");
192            contentHandler.endPrefixMapping("");
193            depth--;
194        }
195    
196        /**
197         * @see nu.validator.htmlparser.impl.TreeBuilder#start()
198         */
199        @Override
200        protected void start(boolean fragment) throws SAXException {
201            contentHandler.setDocumentLocator(tokenizer);
202            if (fragment) {
203                depth = 1;
204            } else {
205                depth = 0;
206                contentHandler.startDocument();
207            }
208        }
209    
210    }