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 }