View Javadoc
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 }