001 /* 002 * Copyright (c) 2007 Henri Sivonen 003 * Copyright (c) 2008 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.saxtree; 025 026 import java.util.LinkedList; 027 import java.util.List; 028 029 import org.xml.sax.Attributes; 030 import org.xml.sax.ContentHandler; 031 import org.xml.sax.Locator; 032 import org.xml.sax.SAXException; 033 import org.xml.sax.ext.LexicalHandler; 034 035 /** 036 * Builds a SAX Tree representation of a document or a fragment 037 * streamed as <code>ContentHandler</code> and 038 * <code>LexicalHandler</code> events. The start/end event matching 039 * is expected to adhere to the SAX API contract. Things will 040 * simply break if this is not the case. Fragments are expected to 041 * omit <code>startDocument()</code> and <code>endDocument()</code> 042 * calls. 043 * 044 * @version $Id$ 045 * @author hsivonen 046 */ 047 public class TreeBuilder implements ContentHandler, LexicalHandler { 048 049 /** 050 * The locator. 051 */ 052 private Locator locator; 053 054 /** 055 * The current node. 056 */ 057 private ParentNode current; 058 059 /** 060 * Whether to retain attribute objects. 061 */ 062 private final boolean retainAttributes; 063 064 /** 065 * The prefix mappings for the next element to be inserted. 066 */ 067 private List<PrefixMapping> prefixMappings; 068 069 /** 070 * Constructs a reusable <code>TreeBuilder</code> that builds 071 * <code>Document</code>s and copies attributes. 072 */ 073 public TreeBuilder() { 074 this(false, false); 075 } 076 077 /** 078 * The constructor. The instance will be reusabe if building a full 079 * document and not reusable if building a fragment. 080 * 081 * @param fragment whether this <code>TreeBuilder</code> should build 082 * a <code>DocumentFragment</code> instead of a <code>Document</code>. 083 * @param retainAttributes whether instances of the <code>Attributes</code> 084 * interface passed to <code>startElement</code> should be retained 085 * (the alternative is copying). 086 */ 087 public TreeBuilder(boolean fragment, boolean retainAttributes) { 088 if (fragment) { 089 current = new DocumentFragment(); 090 } 091 this.retainAttributes = retainAttributes; 092 } 093 094 /** 095 * 096 * @see org.xml.sax.ContentHandler#characters(char[], int, int) 097 */ 098 public void characters(char[] ch, int start, int length) throws SAXException { 099 current.appendChild(new Characters(locator, ch, start, length)); 100 } 101 102 /** 103 * 104 * @see org.xml.sax.ContentHandler#endDocument() 105 */ 106 public void endDocument() throws SAXException { 107 current.setEndLocator(locator); 108 } 109 110 /** 111 * 112 * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String) 113 */ 114 public void endElement(String uri, String localName, String qName) throws SAXException { 115 current.setEndLocator(locator); 116 current = current.getParentNode(); 117 } 118 119 /** 120 * 121 * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String) 122 */ 123 public void endPrefixMapping(String prefix) throws SAXException { 124 } 125 126 /** 127 * 128 * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int) 129 */ 130 public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { 131 current.appendChild(new IgnorableWhitespace(locator, ch, start, length)); 132 } 133 134 /** 135 * 136 * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String) 137 */ 138 public void processingInstruction(String target, String data) throws SAXException { 139 current.appendChild(new ProcessingInstruction(locator, target, data)); 140 } 141 142 /** 143 * 144 * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator) 145 */ 146 public void setDocumentLocator(Locator locator) { 147 this.locator = locator; 148 } 149 150 public void skippedEntity(String name) throws SAXException { 151 current.appendChild(new SkippedEntity(locator, name)); 152 } 153 154 /** 155 * 156 * @see org.xml.sax.ContentHandler#startDocument() 157 */ 158 public void startDocument() throws SAXException { 159 current = new Document(locator); 160 } 161 162 /** 163 * 164 * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) 165 */ 166 public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { 167 current = (ParentNode) current.appendChild(new Element(locator, uri, localName, qName, atts, retainAttributes, prefixMappings)); 168 prefixMappings = null; 169 } 170 171 /** 172 * 173 * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String) 174 */ 175 public void startPrefixMapping(String prefix, String uri) throws SAXException { 176 if (prefixMappings == null) { 177 prefixMappings = new LinkedList<PrefixMapping>(); 178 } 179 prefixMappings.add(new PrefixMapping(prefix, uri)); 180 } 181 182 /** 183 * 184 * @see org.xml.sax.ext.LexicalHandler#comment(char[], int, int) 185 */ 186 public void comment(char[] ch, int start, int length) throws SAXException { 187 current.appendChild(new Comment(locator, ch, start, length)); 188 } 189 190 /** 191 * 192 * @see org.xml.sax.ext.LexicalHandler#endCDATA() 193 */ 194 public void endCDATA() throws SAXException { 195 current.setEndLocator(locator); 196 current = current.getParentNode(); 197 } 198 199 /** 200 * 201 * @see org.xml.sax.ext.LexicalHandler#endDTD() 202 */ 203 public void endDTD() throws SAXException { 204 current.setEndLocator(locator); 205 current = current.getParentNode(); 206 } 207 208 /** 209 * 210 * @see org.xml.sax.ext.LexicalHandler#endEntity(java.lang.String) 211 */ 212 public void endEntity(String name) throws SAXException { 213 current.setEndLocator(locator); 214 current = current.getParentNode(); 215 } 216 217 /** 218 * 219 * @see org.xml.sax.ext.LexicalHandler#startCDATA() 220 */ 221 public void startCDATA() throws SAXException { 222 current = (ParentNode) current.appendChild(new CDATA(locator)); 223 } 224 225 /** 226 * 227 * @see org.xml.sax.ext.LexicalHandler#startDTD(java.lang.String, java.lang.String, java.lang.String) 228 */ 229 public void startDTD(String name, String publicId, String systemId) throws SAXException { 230 current = (ParentNode) current.appendChild(new DTD(locator, name, publicId, systemId)); 231 } 232 233 /** 234 * 235 * @see org.xml.sax.ext.LexicalHandler#startEntity(java.lang.String) 236 */ 237 public void startEntity(String name) throws SAXException { 238 current = (ParentNode) current.appendChild(new Entity(locator, name)); 239 } 240 241 /** 242 * Returns the root (<code>Document</code> if building a full document or 243 * <code>DocumentFragment</code> if building a fragment.). 244 * 245 * @return the root 246 */ 247 public ParentNode getRoot() { 248 return current; 249 } 250 }