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 }