001 /*
002 * Copyright (c) 2008 Mozilla Foundation
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.DocumentType;
026 import org.w3c.dom.NamedNodeMap;
027 import org.w3c.dom.Node;
028 import org.xml.sax.Attributes;
029 import org.xml.sax.ContentHandler;
030 import org.xml.sax.SAXException;
031 import org.xml.sax.ext.LexicalHandler;
032
033 public class Dom2Sax {
034
035 private static String emptyIfNull(String namespaceURI) {
036 return namespaceURI == null ? "" : namespaceURI;
037 }
038
039 private final NamedNodeMapAttributes attributes = new NamedNodeMapAttributes();
040
041 private final ContentHandler contentHandler;
042
043 private final LexicalHandler lexicalHandler;
044
045 /**
046 * @param contentHandler
047 * @param lexicalHandler
048 */
049 public Dom2Sax(ContentHandler contentHandler, LexicalHandler lexicalHandler) {
050 if (contentHandler == null) {
051 throw new IllegalArgumentException("ContentHandler must not be null.");
052 }
053 this.contentHandler = contentHandler;
054 this.lexicalHandler = lexicalHandler;
055 }
056
057 public void parse(Node node) throws SAXException {
058 Node current = node;
059 Node next;
060 char[] buf;
061 for (;;) {
062 switch (current.getNodeType()) {
063 case Node.ELEMENT_NODE:
064 attributes.setNamedNodeMap(current.getAttributes());
065 // To work around severe bogosity in the default DOM
066 // impl, use the node name if local name is null.
067 String localName = current.getLocalName();
068 contentHandler.startElement(
069 emptyIfNull(current.getNamespaceURI()),
070 localName == null ? current.getNodeName()
071 : localName, null, attributes);
072 attributes.clear();
073 break;
074 case Node.TEXT_NODE:
075 buf = current.getNodeValue().toCharArray();
076 contentHandler.characters(buf, 0, buf.length);
077 break;
078 case Node.CDATA_SECTION_NODE:
079 if (lexicalHandler != null) {
080 lexicalHandler.startCDATA();
081 }
082 buf = current.getNodeValue().toCharArray();
083 contentHandler.characters(buf, 0, buf.length);
084 if (lexicalHandler != null) {
085 lexicalHandler.endCDATA();
086 }
087 break;
088 case Node.COMMENT_NODE:
089 if (lexicalHandler != null) {
090 buf = current.getNodeValue().toCharArray();
091 lexicalHandler.comment(buf, 0, buf.length);
092 }
093 break;
094 case Node.DOCUMENT_NODE:
095 contentHandler.startDocument();
096 break;
097 case Node.DOCUMENT_TYPE_NODE:
098 if (lexicalHandler != null) {
099 DocumentType doctype = (DocumentType) current;
100 lexicalHandler.startDTD(doctype.getName(),
101 doctype.getPublicId(), doctype.getSystemId());
102 lexicalHandler.endDTD();
103 }
104 break;
105 case Node.PROCESSING_INSTRUCTION_NODE:
106 contentHandler.processingInstruction(current.getNodeName(), current.getNodeValue());
107 break;
108 case Node.ENTITY_REFERENCE_NODE:
109 contentHandler.skippedEntity(current.getNodeName());
110 break;
111 }
112 if ((next = current.getFirstChild()) != null) {
113 current = next;
114 continue;
115 }
116 for (;;) {
117 switch (current.getNodeType()) {
118 case Node.ELEMENT_NODE:
119 // To work around severe bogosity in the default DOM
120 // impl, use the node name if local name is null.
121 String localName = current.getLocalName();
122 contentHandler.endElement(
123 emptyIfNull(current.getNamespaceURI()),
124 localName == null ? current.getNodeName()
125 : localName, null);
126 break;
127 case Node.DOCUMENT_NODE:
128 contentHandler.endDocument();
129 break;
130 }
131 if (current == node) {
132 return;
133 }
134 if ((next = current.getNextSibling()) != null) {
135 current = next;
136 break;
137 }
138 current = current.getParentNode();
139 }
140 }
141 }
142
143 private class NamedNodeMapAttributes implements Attributes {
144
145 private NamedNodeMap map;
146
147 private int length;
148
149 public void setNamedNodeMap(NamedNodeMap attributes) {
150 this.map = attributes;
151 this.length = attributes.getLength();
152 }
153
154 public void clear() {
155 this.map = null;
156 }
157
158 public int getIndex(String qName) {
159 for (int i = 0; i < length; i++) {
160 Node n = map.item(i);
161 if (n.getNodeName().equals(qName)) {
162 return i;
163 }
164 }
165 return -1;
166 }
167
168 public int getIndex(String uri, String localName) {
169 for (int i = 0; i < length; i++) {
170 Node n = map.item(i);
171 if (n.getLocalName().equals(localName) && emptyIfNull(n.getNamespaceURI()).equals(uri)) {
172 return i;
173 }
174 }
175 return -1;
176 }
177
178 public int getLength() {
179 return length;
180 }
181
182 public String getLocalName(int index) {
183 if (index < length && index >= 0) {
184 return map.item(index).getLocalName();
185 } else {
186 return null;
187 }
188 }
189
190 public String getQName(int index) {
191 if (index < length && index >= 0) {
192 return map.item(index).getNodeName();
193 } else {
194 return null;
195 }
196 }
197
198 public String getType(int index) {
199 if (index < length && index >= 0) {
200 return "id".equals(map.item(index).getLocalName()) ? "ID" : "CDATA";
201 } else {
202 return null;
203 }
204 }
205
206 public String getType(String qName) {
207 int index = getIndex(qName);
208 if (index == -1) {
209 return null;
210 } else {
211 return getType(index);
212 }
213 }
214
215 public String getType(String uri, String localName) {
216 int index = getIndex(uri, localName);
217 if (index == -1) {
218 return null;
219 } else {
220 return getType(index);
221 }
222 }
223
224 public String getURI(int index) {
225 if (index < length && index >= 0) {
226 return emptyIfNull(map.item(index).getNamespaceURI());
227 } else {
228 return null;
229 }
230 }
231
232 public String getValue(int index) {
233 if (index < length && index >= 0) {
234 return map.item(index).getNodeValue();
235 } else {
236 return null;
237 }
238 }
239
240 public String getValue(String qName) {
241 int index = getIndex(qName);
242 if (index == -1) {
243 return null;
244 } else {
245 return getValue(index);
246 }
247 }
248
249 public String getValue(String uri, String localName) {
250 int index = getIndex(uri, localName);
251 if (index == -1) {
252 return null;
253 } else {
254 return getValue(index);
255 }
256 }
257
258 }
259 }