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.dom;
024    
025    import org.w3c.dom.DOMException;
026    import org.w3c.dom.DOMImplementation;
027    import org.w3c.dom.Document;
028    import org.w3c.dom.DocumentFragment;
029    import org.w3c.dom.Element;
030    import org.w3c.dom.Node;
031    import org.xml.sax.Attributes;
032    import org.xml.sax.SAXException;
033    import org.xml.sax.SAXParseException;
034    
035    import nu.validator.htmlparser.common.DocumentMode;
036    import nu.validator.htmlparser.common.DocumentModeHandler;
037    import nu.validator.htmlparser.common.XmlViolationPolicy;
038    import nu.validator.htmlparser.impl.TreeBuilder;
039    
040    class DOMTreeBuilder extends TreeBuilder<Element> {
041    
042        private DOMImplementation implementation;
043    
044        private Document document;
045    
046        protected DOMTreeBuilder(DOMImplementation implementation) {
047            super(XmlViolationPolicy.ALLOW, true);
048            this.implementation = implementation;
049        }
050    
051        @Override
052        protected void addAttributesToElement(Element element, Attributes attributes)
053                throws SAXException {
054            try {
055                for (int i = 0; i < attributes.getLength(); i++) {
056                    String localName = attributes.getLocalName(i);
057                    String uri = attributes.getURI(i);
058                    if (!element.hasAttributeNS(uri, localName)) {
059                        element.setAttributeNS(uri, localName,
060                                attributes.getValue(i));
061                    }
062                }
063            } catch (DOMException e) {
064                fatal(e);
065            }
066        }
067    
068        @Override
069        protected void appendCharacters(Element parent, char[] buf, int start,
070                int length) throws SAXException {
071            try {
072                parent.appendChild(document.createTextNode(new String(buf, start,
073                        length)));
074            } catch (DOMException e) {
075                fatal(e);
076            }
077        }
078    
079        @Override
080        protected void appendChildrenToNewParent(Element oldParent,
081                Element newParent) throws SAXException {
082            try {
083                while (oldParent.hasChildNodes()) {
084                    newParent.appendChild(oldParent.getFirstChild());
085                }
086            } catch (DOMException e) {
087                fatal(e);
088            }
089        }
090    
091        @Override
092        protected void appendComment(Element parent, char[] buf, int start,
093                int length) throws SAXException {
094            try {
095                parent.appendChild(document.createComment(new String(buf, start,
096                        length)));
097            } catch (DOMException e) {
098                fatal(e);
099            }
100        }
101    
102        @Override
103        protected void appendCommentToDocument(char[] buf, int start, int length)
104                throws SAXException {
105            try {
106                document.appendChild(document.createComment(new String(buf, start,
107                        length)));
108            } catch (DOMException e) {
109                fatal(e);
110            }
111        }
112    
113        @Override
114        protected Element createElement(String name, Attributes attributes)
115                throws SAXException {
116            try {
117                Element rv = document.createElementNS(
118                        "http://www.w3.org/1999/xhtml", name);
119                for (int i = 0; i < attributes.getLength(); i++) {
120                    rv.setAttributeNS(attributes.getURI(i),
121                            attributes.getLocalName(i), attributes.getValue(i));
122                }
123                return rv;
124            } catch (DOMException e) {
125                fatal(e);
126                throw new RuntimeException("Unreachable");
127            }
128        }
129    
130        @Override
131        protected Element createHtmlElementSetAsRoot(Attributes attributes)
132                throws SAXException {
133            try {
134                Element rv = document.createElementNS(
135                        "http://www.w3.org/1999/xhtml", "html");
136                for (int i = 0; i < attributes.getLength(); i++) {
137                    rv.setAttributeNS(attributes.getURI(i),
138                            attributes.getLocalName(i), attributes.getValue(i));
139                }
140                document.appendChild(rv);
141                return rv;
142            } catch (DOMException e) {
143                fatal(e);
144                throw new RuntimeException("Unreachable");
145            }
146        }
147    
148        @Override
149        protected void detachFromParent(Element element) throws SAXException {
150            try {
151                Node parent = element.getParentNode();
152                if (parent != null) {
153                    parent.removeChild(element);
154                }
155            } catch (DOMException e) {
156                fatal(e);
157            }
158        }
159    
160        @Override
161        protected void detachFromParentAndAppendToNewParent(Element child,
162                Element newParent) throws SAXException {
163            try {
164                newParent.appendChild(child);
165            } catch (DOMException e) {
166                fatal(e);
167            }
168        }
169    
170        @Override
171        protected boolean hasChildren(Element element) throws SAXException {
172            try {
173                return element.hasChildNodes();
174            } catch (DOMException e) {
175                fatal(e);
176                throw new RuntimeException("Unreachable");
177            }
178        }
179    
180        @Override
181        protected void insertBefore(Element child, Element sibling, Element parent)
182                throws SAXException {
183            try {
184                parent.insertBefore(child, sibling);
185            } catch (DOMException e) {
186                fatal(e);
187            }
188        }
189    
190        @Override
191        protected void insertCharactersBefore(char[] buf, int start, int length,
192                Element sibling, Element parent) throws SAXException {
193            try {
194                parent.insertBefore(document.createTextNode(new String(buf, start, length)), sibling);
195            } catch (DOMException e) {
196                fatal(e);
197            }
198        }
199    
200        @Override
201        protected Element parentElementFor(Element child) throws SAXException {
202            try {
203                Node parent = child.getParentNode();
204                if (parent != null && parent.getNodeType() == Node.ELEMENT_NODE) {
205                    return (Element) parent;
206                } else {
207                    return null;
208                }
209            } catch (DOMException e) {
210                fatal(e);
211                throw new RuntimeException("Unreachable");
212            }
213        }
214    
215        @Override
216        protected Element shallowClone(Element element) throws SAXException {
217            try {
218                return (Element) element.cloneNode(false);
219            } catch (DOMException e) {
220                fatal(e);
221                throw new RuntimeException("Unreachable");
222            }
223        }
224    
225        /**
226         * Returns the document.
227         * 
228         * @return the document
229         */
230        Document getDocument() {
231            Document rv = document;
232            document = null;
233            return rv;
234        }
235    
236        DocumentFragment getDocumentFragment() {
237            DocumentFragment rv = document.createDocumentFragment();
238            Node rootElt = document.getFirstChild();
239            while (rootElt.hasChildNodes()) {
240                rv.appendChild(rootElt.getFirstChild());
241            }
242            document = null;
243            return rv;
244        }
245    
246        /**
247         * @see nu.validator.htmlparser.impl.TreeBuilder#createElement(java.lang.String, org.xml.sax.Attributes, java.lang.Object)
248         */
249        @Override
250        protected Element createElement(String name, Attributes attributes,
251                Element form) throws SAXException {
252            try {
253                Element rv = createElement(name, attributes);
254                rv.setUserData("nu.validator.form-pointer", form, null);
255                return rv;
256            } catch (DOMException e) {
257                fatal(e);
258                return null;
259            }
260        }
261    
262        /**
263         * @see nu.validator.htmlparser.impl.TreeBuilder#start()
264         */
265        @Override
266        protected void start(boolean fragment) throws SAXException {
267            document = implementation.createDocument(null, null, null);
268        }
269    
270        protected void documentMode(DocumentMode mode, String publicIdentifier, String systemIdentifier, boolean html4SpecificAdditionalErrorChecks) throws SAXException {
271            document.setUserData("nu.validator.document-mode", mode, null);
272        }
273    }