001    /*
002     * Copyright (c) 2007 Henri Sivonen
003     * Copyright (c) 2008-2009 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.htmlparser.sax;
025    
026    import nu.validator.htmlparser.impl.HtmlAttributes;
027    import nu.validator.htmlparser.impl.TreeBuilder;
028    import nu.validator.saxtree.Characters;
029    import nu.validator.saxtree.Comment;
030    import nu.validator.saxtree.DTD;
031    import nu.validator.saxtree.Document;
032    import nu.validator.saxtree.DocumentFragment;
033    import nu.validator.saxtree.Element;
034    import nu.validator.saxtree.Node;
035    import nu.validator.saxtree.ParentNode;
036    
037    import org.xml.sax.SAXException;
038    
039    class SAXTreeBuilder extends TreeBuilder<Element> {
040        
041        private Document document;
042        
043        private Node cachedTable = null;
044        
045        private Node cachedTablePreviousSibling = null;
046        
047        SAXTreeBuilder() {
048            super();
049        }
050        
051        @Override
052        protected void appendComment(Element parent, char[] buf, int start, int length) {
053            parent.appendChild(new Comment(tokenizer, buf, start, length));
054        }
055    
056        @Override
057        protected void appendCommentToDocument(char[] buf, int start, int length) {
058            document.appendChild(new Comment(tokenizer, buf, start, length));
059        }
060    
061        @Override
062        protected void appendCharacters(Element parent, char[] buf, int start, int length) {
063            parent.appendChild(new Characters(tokenizer, buf, start, length));
064        }
065    
066        @Override
067        protected boolean hasChildren(Element element) {
068            return element.getFirstChild() != null;
069        }
070    
071        @Override
072        protected Element shallowClone(Element element) {
073            Element newElt =  new Element(element, element.getUri(), element.getLocalName(), element.getQName(), element.getAttributes(), true, element.getPrefixMappings());
074            newElt.copyEndLocator(element);
075            return newElt;
076        }
077    
078        @Override
079        protected void appendElement(Element child, Element newParent) {
080            newParent.appendChild(child);
081        }
082    
083        @Override
084        protected Element createHtmlElementSetAsRoot(HtmlAttributes attributes) {
085            Element newElt = new Element(tokenizer, "http://www.w3.org/1999/xhtml", "html", "html", attributes, true, null);
086            document.appendChild(newElt);
087            return newElt;
088        }
089    
090        @Override
091        protected void addAttributesToElement(Element element, HtmlAttributes attributes) throws SAXException {
092            HtmlAttributes existingAttrs = (HtmlAttributes) element.getAttributes();
093            existingAttrs.merge(attributes);
094        }
095    
096        /**
097         * @see nu.validator.htmlparser.impl.TreeBuilder#appendDoctypeToDocument(java.lang.String, java.lang.String, java.lang.String)
098         */
099        @Override
100        protected void appendDoctypeToDocument(String name, String publicIdentifier, String systemIdentifier) {
101             DTD dtd = new DTD(tokenizer, name, publicIdentifier, systemIdentifier);
102             dtd.setEndLocator(tokenizer);
103             document.appendChild(dtd);
104        }
105        
106        /**
107         * Returns the document.
108         * 
109         * @return the document
110         */
111        Document getDocument() {
112            Document rv = document;
113            document = null;
114            return rv;
115        }
116        
117        DocumentFragment getDocumentFragment() {
118            DocumentFragment rv = new DocumentFragment();
119            rv.appendChildren(document.getFirstChild());
120            document = null;
121            return rv;
122        }
123    
124        /**
125         * @throws SAXException 
126         * @see nu.validator.htmlparser.impl.TreeBuilder#end()
127         */
128        @Override
129        protected void end() throws SAXException {
130            document.setEndLocator(tokenizer);
131            cachedTable = null;
132            cachedTablePreviousSibling = null;
133        }
134    
135        /**
136         * @see nu.validator.htmlparser.impl.TreeBuilder#start()
137         */
138        @Override
139        protected void start(boolean fragment) {
140            document = new Document(tokenizer);
141            cachedTable = null;
142            cachedTablePreviousSibling = null;
143        }
144    
145        @Override
146        protected void appendChildrenToNewParent(Element oldParent, Element newParent) throws SAXException {
147            newParent.appendChildren(oldParent);
148        }
149    
150        @Override
151        protected Element createElement(String ns, String name, HtmlAttributes attributes) throws SAXException {
152            return new Element(tokenizer, ns, name, name, attributes, true, null);
153        }
154    
155        @Override protected void insertFosterParentedCharacters(char[] buf,
156                int start, int length, Element table, Element stackParent) throws SAXException {
157            Node child = new Characters(tokenizer, buf, start, length);
158            ParentNode parent = table.getParentNode();
159            if (parent != null) { // always an element if not null
160                parent.insertBetween(child, previousSibling(table), table);
161                cachedTablePreviousSibling = child;
162            } else {
163                stackParent.appendChild(child);
164            }        
165        }
166    
167        @Override protected void insertFosterParentedChild(Element child,
168                Element table, Element stackParent) throws SAXException {
169            ParentNode parent = table.getParentNode();
170            if (parent != null) { // always an element if not null
171                parent.insertBetween(child, previousSibling(table), table);
172                cachedTablePreviousSibling = child;
173            } else {
174                stackParent.appendChild(child);
175            }
176        }
177    
178        private Node previousSibling(Node table) {
179            if (table == cachedTable) {
180                return cachedTablePreviousSibling;   
181            } else {
182                cachedTable = table;
183                return (cachedTablePreviousSibling = table.getPreviousSibling());
184            }
185        }
186    
187        @Override protected void detachFromParent(Element element)
188                throws SAXException {
189            element.detach();
190        }
191    }