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.saxtree; 024 025 import java.util.LinkedList; 026 import java.util.List; 027 028 import org.xml.sax.Attributes; 029 import org.xml.sax.ContentHandler; 030 import org.xml.sax.Locator; 031 import org.xml.sax.SAXException; 032 import org.xml.sax.ext.LexicalHandler; 033 034 /** 035 * Builds a SAX Tree representation of a document or a fragment 036 * streamed as <code>ContentHandler</code> and 037 * <code>LexicalHandler</code> events. The start/end event matching 038 * is expected to adhere to the SAX API contract. Things will 039 * simply break if this is not the case. Fragments are expected to 040 * omit <code>startDocument()</code> and <code>endDocument()</code> 041 * calls. 042 * 043 * @version $Id: TreeBuilder.java 150 2007-08-16 19:21:25Z hsivonen $ 044 * @author hsivonen 045 */ 046 public class TreeBuilder implements ContentHandler, LexicalHandler { 047 048 private Locator locator; 049 050 private ParentNode current; 051 052 private final boolean retainAttributes; 053 054 private List<PrefixMapping> prefixMappings; 055 056 /** 057 * Constructs a reusable <code>TreeBuilder</code> that builds 058 * <code>Document</code>s and copies attributes. 059 */ 060 public TreeBuilder() { 061 this(false, false); 062 } 063 064 /** 065 * The constructor. The instance will be reusabe if building a full 066 * document and not reusable if building a fragment. 067 * 068 * @param fragment whether this <code>TreeBuilder</code> should build 069 * a <code>DocumentFragment</code> instead of a <code>Document</code>. 070 * @param retainAttributes whether instances of the <code>Attributes</code> 071 * interface passed to <code>startElement</code> should be retained 072 * (the alternative is copying). 073 */ 074 public TreeBuilder(boolean fragment, boolean retainAttributes) { 075 if (fragment) { 076 current = new DocumentFragment(); 077 } 078 this.retainAttributes = retainAttributes; 079 } 080 081 public void characters(char[] ch, int start, int length) throws SAXException { 082 current.appendChild(new Characters(locator, ch, start, length)); 083 } 084 085 public void endDocument() throws SAXException { 086 current.setEndLocator(locator); 087 } 088 089 public void endElement(String uri, String localName, String qName) throws SAXException { 090 current.setEndLocator(locator); 091 current = current.getParentNode(); 092 } 093 094 public void endPrefixMapping(String prefix) throws SAXException { 095 } 096 097 public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { 098 current.appendChild(new IgnorableWhitespace(locator, ch, start, length)); 099 } 100 101 public void processingInstruction(String target, String data) throws SAXException { 102 current.appendChild(new ProcessingInstruction(locator, target, data)); 103 } 104 105 public void setDocumentLocator(Locator locator) { 106 this.locator = locator; 107 } 108 109 public void skippedEntity(String name) throws SAXException { 110 current.appendChild(new SkippedEntity(locator, name)); 111 } 112 113 public void startDocument() throws SAXException { 114 current = new Document(locator); 115 } 116 117 public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { 118 current = (ParentNode) current.appendChild(new Element(locator, uri, localName, qName, atts, retainAttributes, prefixMappings)); 119 prefixMappings = null; 120 } 121 122 public void startPrefixMapping(String prefix, String uri) throws SAXException { 123 if (prefixMappings == null) { 124 prefixMappings = new LinkedList<PrefixMapping>(); 125 } 126 prefixMappings.add(new PrefixMapping(prefix, uri)); 127 } 128 129 public void comment(char[] ch, int start, int length) throws SAXException { 130 current.appendChild(new Comment(locator, ch, start, length)); 131 } 132 133 public void endCDATA() throws SAXException { 134 current.setEndLocator(locator); 135 current = current.getParentNode(); 136 } 137 138 public void endDTD() throws SAXException { 139 current.setEndLocator(locator); 140 current = current.getParentNode(); 141 } 142 143 public void endEntity(String name) throws SAXException { 144 current.setEndLocator(locator); 145 current = current.getParentNode(); 146 } 147 148 public void startCDATA() throws SAXException { 149 current = (ParentNode) current.appendChild(new CDATA(locator)); 150 } 151 152 public void startDTD(String name, String publicId, String systemId) throws SAXException { 153 current = (ParentNode) current.appendChild(new DTD(locator, name, publicId, systemId)); 154 } 155 156 public void startEntity(String name) throws SAXException { 157 current = (ParentNode) current.appendChild(new Entity(locator, name)); 158 } 159 160 /** 161 * Returns the root (<code>Document</code> if building a full document or 162 * <code>DocumentFragment</code> if building a fragment.). 163 * 164 * @return the root 165 */ 166 public ParentNode getRoot() { 167 return current; 168 } 169 }