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 }