1 /*
2 * Copyright 2007 Edward Kuns
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * $Id: XMLBuilder.java 0000 2007-01-11 00:00:00Z ekuns $
17 */
18 package org.castor.xmlctf.xmldiff.xml;
19
20 import java.util.HashMap;
21 import java.util.Iterator;
22 import java.util.Map;
23 import java.util.Stack;
24
25 import org.castor.xmlctf.xmldiff.xml.nodes.Attribute;
26 import org.castor.xmlctf.xmldiff.xml.nodes.Element;
27 import org.castor.xmlctf.xmldiff.xml.nodes.Namespace;
28 import org.castor.xmlctf.xmldiff.xml.nodes.ParentNode;
29 import org.castor.xmlctf.xmldiff.xml.nodes.ProcessingInstruction;
30 import org.castor.xmlctf.xmldiff.xml.nodes.Root;
31 import org.castor.xmlctf.xmldiff.xml.nodes.Text;
32 import org.xml.sax.Attributes;
33 import org.xml.sax.ContentHandler;
34 import org.xml.sax.Locator;
35 import org.xml.sax.SAXException;
36
37 /**
38 * A ContentHandler implementation that builds a tree of XMLNodes.
39 *
40 * @author <a href="mailto:edward.kuns@aspect.com">Edward Kuns</a>
41 * @version $Revision: 0000 $ $Date: 2007-01-11 00:00:00 -0600 (Thu, 11 Jan 2007) $
42 * @since Castor 1.1
43 */
44 public class XMLContentHandler implements ContentHandler {
45
46 /** The current node stack. */
47 private final Stack _nodeStack = new Stack();
48 /** Root BaseNode, the BaseNode being created.. */
49 private final Root _root = new Root();
50
51 /** SAX document Locator, set by a SAX Parser. */
52 private Locator _locator = null;
53 /** The current node to which we are adding content. */
54 private ParentNode _currentNode = null;
55 /** Keeps track of all URL mapping prefixes currently defined. */
56 private Map _prefixes = new HashMap();
57
58 /**
59 * Creates a new XMLBuilder.
60 */
61 public XMLContentHandler() {
62 _nodeStack.push(_root);
63 _currentNode = _root;
64 }
65
66 /**
67 * Creates a new text node from incoming characters.
68 *
69 * @param chars The character array containing the XML content
70 * @param start First index of character for our new Text node
71 * @param length count of characters for our Text node.
72 * @throws org.xml.sax.SAXException never
73 */
74 public void characters(final char[] chars, final int start, final int length) throws org.xml.sax.SAXException {
75 _currentNode.addChild(new Text(new String(chars, start, length)));
76 }
77
78 /**
79 * Signals the end of the document.
80 * @throws org.xml.sax.SAXException never
81 */
82 public void endDocument() throws org.xml.sax.SAXException {
83 // Nothing to do
84 }
85
86 /**
87 * Signals the end of an Element.
88 * @param uri The namespace URI
89 * @param name the local name of the element.
90 * @param qName the qualified naem of the element
91 * @throws org.xml.sax.SAXException if we have a mismatched end element tag
92 */
93 public void endElement(final String uri, final String name, final String qName)
94 throws org.xml.sax.SAXException {
95 final String localName = name;
96
97 final int idx = qName.indexOf(':');
98 final String prefix = (idx >= 0) ? qName.substring(0, idx) : "";
99
100 // Check the prefix to make sure it is appropriate
101 String uriOfPrefix = _currentNode.getNamespaceURI(prefix);
102 String uriOfElement = _currentNode.getNamespaceURI();
103 if ((uriOfPrefix == null ^ uriOfElement == null)
104 || (uriOfPrefix != null && !uriOfPrefix.equals(uriOfElement))) {
105 throw new org.xml.sax.SAXException("In Element " + qName + ", URI of prefix " + uriOfPrefix +
106 " does not match URI of Element " + uriOfElement);
107 }
108
109 String cName = _currentNode.getLocalName();
110 if (!cName.equals(localName)) {
111 String err = "Element end tag mismatch: expecting </" + cName
112 + "> but recieved </" + localName + ">";
113 throw new SAXException(err);
114 }
115
116 _nodeStack.pop();
117 _currentNode = (ParentNode) _nodeStack.peek();
118 }
119
120 /**
121 * Signals the end of prefix mapping.
122 * @param prefix The namespace prefix mapping that is ending
123 * @throws org.xml.sax.SAXException never
124 */
125 public void endPrefixMapping(final String prefix) throws SAXException {
126 _prefixes.remove(prefix);
127 }
128
129 /**
130 * Returns the Root node.
131 * @return the root node.
132 */
133 public Root getRoot() {
134 return _root;
135 }
136
137 /**
138 * Ignores ignorable whitespace.
139 *
140 * @param chars The character array containing the XML content
141 * @param start First index of the ignorable whitespace
142 * @param length count of characters for the ignorable whitespace
143 * @throws org.xml.sax.SAXException never
144 */
145 public void ignorableWhitespace(final char[] chars, final int start, final int length)
146 throws org.xml.sax.SAXException {
147 // Deliberately ignore -- we don't care
148 }
149
150 /**
151 * Creates a new Processing Instruction node.
152 *
153 * @param target the target of the processing instruction
154 * @param data the content of the processing instruction
155 * @throws org.xml.sax.SAXException never
156 */
157 public void processingInstruction(final String target, final String data)
158 throws org.xml.sax.SAXException {
159 ProcessingInstruction pi = new ProcessingInstruction(target, data);
160 _currentNode.addChild(pi);
161 }
162
163 /**
164 * Configures the Locator we will use.
165 *
166 * @param locator the Locator used by this DocumentHandler.
167 */
168 public void setDocumentLocator(final Locator locator) {
169 _locator = locator;
170 }
171
172 /**
173 * Gives notification about a skipped Entity during XML parsing.
174 * @param name the name of the skipped entity.
175 */
176 public void skippedEntity(final String name) {
177 // Nothing to do
178 }
179
180 /**
181 * Signals the beginning of the document.
182 * @throws org.xml.sax.SAXException never
183 */
184 public void startDocument() throws SAXException {
185 // Nothing to do
186 }
187
188 /**
189 * Signals the beginning of an Element node.
190 *
191 * @param uri The namespace URI
192 * @param name the local name of the element.
193 * @param qName the qualified naem of the element
194 * @param atts a list of attributes for this Element
195 * @throws org.xml.sax.SAXException If we are not given an element name.
196 */
197 public void startElement(final String uri, final String name, final String qName, final Attributes atts) throws org.xml.sax.SAXException {
198 if (qName == null) {
199 throw new SAXException("No Element name given");
200 }
201
202 final String prefix;
203 final String localName;
204
205 int idx = qName.indexOf(':');
206 if (idx >= 0) {
207 prefix = qName.substring(0, idx);
208 localName = qName.substring(idx + 1);
209 } else {
210 prefix = "";
211 localName = qName;
212 }
213
214 Element element = new Element(null, localName);
215
216 if (_locator != null) {
217 element.setLocation(new Location(_locator));
218 }
219
220 _currentNode.addChild(element);
221
222 // Add all current namespaces to this element
223 for (Iterator i = _prefixes.entrySet().iterator(); i.hasNext(); ) {
224 Map.Entry me = (Map.Entry) i.next();
225 element.addNamespace(new Namespace((String)me.getKey(), (String) me.getValue()));
226 }
227
228 // Then add all attributes
229 if (atts != null && atts.getLength() > 0) {
230 for (int i = 0; i < atts.getLength(); i++) {
231 String attName = atts.getQName(i);
232 String ns = null;
233 idx = attName.indexOf(':');
234 if (idx > 0) {
235 ns = element.getNamespaceURI(attName.substring(0, idx));
236 attName = attName.substring(idx + 1);
237 }
238 element.addAttribute(new Attribute(ns, attName, atts.getValue(i)));
239 }
240 }
241
242 // Set the namespace on this Element, if one is explicit or defaulted
243 if (prefix != null && prefix.length() > 0) {
244 String namespace = element.getNamespaceURI(prefix);
245 element.setNamespace(namespace);
246 } else {
247 String namespace = element.getNamespaceURI("");
248 if (namespace != null) {
249 element.setNamespace(namespace);
250 }
251 }
252
253 _nodeStack.push(element);
254 _currentNode = element;
255 }
256
257 /**
258 * Begins the scope of a prefix-URI Namespace mapping.
259 * @param prefix The namespace prefix mapping that is ending
260 * @param uri The namespace URI
261 * @throws org.xml.sax.SAXException never
262 */
263 public void startPrefixMapping(final String prefix, final String uri) {
264 _prefixes.put(prefix, uri);
265 }
266
267 }