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.xom; 024 025 import nu.validator.htmlparser.common.DocumentMode; 026 import nu.validator.htmlparser.common.XmlViolationPolicy; 027 import nu.validator.htmlparser.impl.TreeBuilder; 028 import nu.xom.Attribute; 029 import nu.xom.Document; 030 import nu.xom.Element; 031 import nu.xom.Nodes; 032 import nu.xom.ParentNode; 033 import nu.xom.XMLException; 034 035 import org.xml.sax.Attributes; 036 import org.xml.sax.SAXException; 037 038 class XOMTreeBuilder extends TreeBuilder<Element> { 039 040 private final SimpleNodeFactory nodeFactory; 041 042 private Document document; 043 044 protected XOMTreeBuilder(SimpleNodeFactory nodeFactory) { 045 super(XmlViolationPolicy.ALLOW, true); 046 this.nodeFactory = nodeFactory; 047 } 048 049 @Override 050 protected void addAttributesToElement(Element element, Attributes attributes) 051 throws SAXException { 052 try { 053 for (int i = 0; i < attributes.getLength(); i++) { 054 String localName = attributes.getLocalName(i); 055 String uri = attributes.getURI(i); 056 if (element.getAttribute(localName, uri) == null) { 057 element.addAttribute(nodeFactory.makeAttribute(localName, 058 uri, attributes.getValue(i), 059 attributes.getType(i) == "ID" ? Attribute.Type.ID 060 : Attribute.Type.CDATA)); 061 } 062 } 063 } catch (XMLException 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(nodeFactory.makeText(new String(buf, start, 073 length))); 074 } catch (XMLException e) { 075 fatal(e); 076 } 077 } 078 079 @Override 080 protected void appendChildrenToNewParent(Element oldParent, 081 Element newParent) throws SAXException { 082 try { 083 Nodes children = oldParent.removeChildren(); 084 for (int i = 0; i < children.size(); i++) { 085 newParent.appendChild(children.get(i)); 086 } 087 } catch (XMLException e) { 088 fatal(e); 089 } 090 } 091 092 @Override 093 protected void appendComment(Element parent, char[] buf, int start, 094 int length) throws SAXException { 095 try { 096 parent.appendChild(nodeFactory.makeComment(new String(buf, start, 097 length))); 098 } catch (XMLException e) { 099 fatal(e); 100 } 101 } 102 103 @Override 104 protected void appendCommentToDocument(char[] buf, int start, int length) 105 throws SAXException { 106 try { 107 Element root = document.getRootElement(); 108 if ("http://www.xom.nu/fakeRoot".equals(root.getNamespaceURI())) { 109 document.insertChild(nodeFactory.makeComment(new String(buf, 110 start, length)), document.indexOf(root)); 111 } else { 112 document.appendChild(nodeFactory.makeComment(new String(buf, 113 start, length))); 114 } 115 } catch (XMLException e) { 116 fatal(e); 117 } 118 } 119 120 @Override 121 protected Element createElement(String name, Attributes attributes) 122 throws SAXException { 123 try { 124 Element rv = nodeFactory.makeElement(name, 125 "http://www.w3.org/1999/xhtml"); 126 for (int i = 0; i < attributes.getLength(); i++) { 127 rv.addAttribute(nodeFactory.makeAttribute( 128 attributes.getLocalName(i), attributes.getURI(i), 129 attributes.getValue(i), 130 attributes.getType(i) == "ID" ? Attribute.Type.ID 131 : Attribute.Type.CDATA)); 132 } 133 return rv; 134 } catch (XMLException e) { 135 fatal(e); 136 throw new RuntimeException("Unreachable"); 137 } 138 } 139 140 @Override 141 protected Element createHtmlElementSetAsRoot(Attributes attributes) 142 throws SAXException { 143 try { 144 Element rv = nodeFactory.makeElement("html", 145 "http://www.w3.org/1999/xhtml"); 146 for (int i = 0; i < attributes.getLength(); i++) { 147 rv.addAttribute(nodeFactory.makeAttribute( 148 attributes.getLocalName(i), attributes.getURI(i), 149 attributes.getValue(i), 150 attributes.getType(i) == "ID" ? Attribute.Type.ID 151 : Attribute.Type.CDATA)); 152 } 153 document.setRootElement(rv); 154 return rv; 155 } catch (XMLException e) { 156 fatal(e); 157 throw new RuntimeException("Unreachable"); 158 } 159 } 160 161 @Override 162 protected void detachFromParent(Element element) throws SAXException { 163 try { 164 element.detach(); 165 } catch (XMLException e) { 166 fatal(e); 167 } 168 } 169 170 @Override 171 protected void detachFromParentAndAppendToNewParent(Element child, 172 Element newParent) throws SAXException { 173 try { 174 child.detach(); 175 newParent.appendChild(child); 176 } catch (XMLException e) { 177 fatal(e); 178 } 179 } 180 181 @Override 182 protected boolean hasChildren(Element element) throws SAXException { 183 try { 184 return element.getChildCount() != 0; 185 } catch (XMLException e) { 186 fatal(e); 187 throw new RuntimeException("Unreachable"); 188 } 189 } 190 191 @Override 192 protected void insertBefore(Element child, Element sibling, Element parent) 193 throws SAXException { 194 try { 195 parent.insertChild(child, parent.indexOf(sibling)); 196 } catch (XMLException e) { 197 fatal(e); 198 } 199 } 200 201 @Override 202 protected void insertCharactersBefore(char[] buf, int start, int length, 203 Element sibling, Element parent) throws SAXException { 204 try { 205 parent.insertChild(nodeFactory.makeText(new String(buf, start, 206 length)), parent.indexOf(sibling)); 207 } catch (XMLException e) { 208 fatal(e); 209 } 210 } 211 212 @Override 213 protected Element parentElementFor(Element child) throws SAXException { 214 try { 215 ParentNode parent = child.getParent(); 216 if (parent != null && parent instanceof Element) { 217 return (Element) parent; 218 } else { 219 return null; 220 } 221 } catch (XMLException e) { 222 fatal(e); 223 throw new RuntimeException("Unreachable"); 224 } 225 } 226 227 @Override 228 protected Element shallowClone(Element element) throws SAXException { 229 try { 230 Element rv = nodeFactory.makeElement(element.getLocalName(), 231 element.getNamespaceURI()); 232 for (int i = 0; i < element.getAttributeCount(); i++) { 233 Attribute attribute = element.getAttribute(i); 234 rv.addAttribute(nodeFactory.makeAttribute( 235 attribute.getLocalName(), attribute.getNamespaceURI(), 236 attribute.getValue(), attribute.getType())); 237 } 238 return rv; 239 } catch (XMLException e) { 240 fatal(e); 241 throw new RuntimeException("Unreachable"); 242 } 243 } 244 245 /** 246 * Returns the document. 247 * 248 * @return the document 249 */ 250 Document getDocument() { 251 Document rv = document; 252 document = null; 253 return rv; 254 } 255 256 Nodes getDocumentFragment() { 257 Element rootElt = document.getRootElement(); 258 Nodes rv = rootElt.removeChildren(); 259 document = null; 260 return rv; 261 } 262 263 /** 264 * @see nu.validator.htmlparser.impl.TreeBuilder#createElement(java.lang.String, 265 * org.xml.sax.Attributes, java.lang.Object) 266 */ 267 @Override 268 protected Element createElement(String name, Attributes attributes, 269 Element form) throws SAXException { 270 try { 271 Element rv = nodeFactory.makeElement(name, 272 "http://www.w3.org/1999/xhtml", form); 273 for (int i = 0; i < attributes.getLength(); i++) { 274 rv.addAttribute(nodeFactory.makeAttribute( 275 attributes.getLocalName(i), attributes.getURI(i), 276 attributes.getValue(i), 277 attributes.getType(i) == "ID" ? Attribute.Type.ID 278 : Attribute.Type.CDATA)); 279 } 280 return rv; 281 } catch (XMLException e) { 282 fatal(e); 283 throw new RuntimeException("Unreachable"); 284 } 285 } 286 287 /** 288 * @see nu.validator.htmlparser.impl.TreeBuilder#start() 289 */ 290 @Override 291 protected void start(boolean fragment) throws SAXException { 292 document = nodeFactory.makeDocument(); 293 } 294 295 /** 296 * @see nu.validator.htmlparser.impl.TreeBuilder#documentMode(nu.validator.htmlparser.common.DocumentMode, 297 * java.lang.String, java.lang.String, boolean) 298 */ 299 @Override 300 protected void documentMode(DocumentMode mode, String publicIdentifier, 301 String systemIdentifier, boolean html4SpecificAdditionalErrorChecks) 302 throws SAXException { 303 if (document instanceof Mode) { 304 Mode modal = (Mode) document; 305 modal.setMode(mode); 306 } 307 } 308 }