/*
* Copyright 2007 Edward Kuns
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Id: XMLBuilder.java 0000 2007-01-11 00:00:00Z ekuns $
*/
package org.castor.xmlctf.xmldiff.xml;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;
import org.castor.xmlctf.xmldiff.xml.nodes.Attribute;
import org.castor.xmlctf.xmldiff.xml.nodes.Element;
import org.castor.xmlctf.xmldiff.xml.nodes.Namespace;
import org.castor.xmlctf.xmldiff.xml.nodes.ParentNode;
import org.castor.xmlctf.xmldiff.xml.nodes.ProcessingInstruction;
import org.castor.xmlctf.xmldiff.xml.nodes.Root;
import org.castor.xmlctf.xmldiff.xml.nodes.Text;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
/**
* A ContentHandler implementation that builds a tree of XMLNodes.
*
* @author <a href="mailto:edward.kuns@aspect.com">Edward Kuns</a>
* @version $Revision: 0000 $ $Date: 2007-01-11 00:00:00 -0600 (Thu, 11 Jan 2007) $
* @since Castor 1.1
*/
public class XMLContentHandler implements ContentHandler {
/** The current node stack. */
private final Stack _nodeStack = new Stack();
/** Root BaseNode, the BaseNode being created.. */
private final Root _root = new Root();
/** SAX document Locator, set by a SAX Parser. */
private Locator _locator = null;
/** The current node to which we are adding content. */
private ParentNode _currentNode = null;
/** Keeps track of all URL mapping prefixes currently defined. */
private Map _prefixes = new HashMap();
/**
* Creates a new XMLBuilder.
*/
public XMLContentHandler() {
_nodeStack.push(_root);
_currentNode = _root;
}
/**
* Creates a new text node from incoming characters.
*
* @param chars The character array containing the XML content
* @param start First index of character for our new Text node
* @param length count of characters for our Text node.
* @throws org.xml.sax.SAXException never
*/
public void characters(final char[] chars, final int start, final int length) throws org.xml.sax.SAXException {
_currentNode.addChild(new Text(new String(chars, start, length)));
}
/**
* Signals the end of the document.
* @throws org.xml.sax.SAXException never
*/
public void endDocument() throws org.xml.sax.SAXException {
// Nothing to do
}
/**
* Signals the end of an Element.
* @param uri The namespace URI
* @param name the local name of the element.
* @param qName the qualified naem of the element
* @throws org.xml.sax.SAXException if we have a mismatched end element tag
*/
public void endElement(final String uri, final String name, final String qName)
throws org.xml.sax.SAXException {
final String localName = name;
final int idx = qName.indexOf(':');
final String prefix = (idx >= 0) ? qName.substring(0, idx) : "";
// Check the prefix to make sure it is appropriate
String uriOfPrefix = _currentNode.getNamespaceURI(prefix);
String uriOfElement = _currentNode.getNamespaceURI();
if ((uriOfPrefix == null ^ uriOfElement == null)
|| (uriOfPrefix != null && !uriOfPrefix.equals(uriOfElement))) {
throw new org.xml.sax.SAXException("In Element " + qName + ", URI of prefix " + uriOfPrefix +
" does not match URI of Element " + uriOfElement);
}
String cName = _currentNode.getLocalName();
if (!cName.equals(localName)) {
String err = "Element end tag mismatch: expecting </" + cName
+ "> but recieved </" + localName + ">";
throw new SAXException(err);
}
_nodeStack.pop();
_currentNode = (ParentNode) _nodeStack.peek();
}
/**
* Signals the end of prefix mapping.
* @param prefix The namespace prefix mapping that is ending
* @throws org.xml.sax.SAXException never
*/
public void endPrefixMapping(final String prefix) throws SAXException {
_prefixes.remove(prefix);
}
/**
* Returns the Root node.
* @return the root node.
*/
public Root getRoot() {
return _root;
}
/**
* Ignores ignorable whitespace.
*
* @param chars The character array containing the XML content
* @param start First index of the ignorable whitespace
* @param length count of characters for the ignorable whitespace
* @throws org.xml.sax.SAXException never
*/
public void ignorableWhitespace(final char[] chars, final int start, final int length)
throws org.xml.sax.SAXException {
// Deliberately ignore -- we don't care
}
/**
* Creates a new Processing Instruction node.
*
* @param target the target of the processing instruction
* @param data the content of the processing instruction
* @throws org.xml.sax.SAXException never
*/
public void processingInstruction(final String target, final String data)
throws org.xml.sax.SAXException {
ProcessingInstruction pi = new ProcessingInstruction(target, data);
_currentNode.addChild(pi);
}
/**
* Configures the Locator we will use.
*
* @param locator the Locator used by this DocumentHandler.
*/
public void setDocumentLocator(final Locator locator) {
_locator = locator;
}
/**
* Gives notification about a skipped Entity during XML parsing.
* @param name the name of the skipped entity.
*/
public void skippedEntity(final String name) {
// Nothing to do
}
/**
* Signals the beginning of the document.
* @throws org.xml.sax.SAXException never
*/
public void startDocument() throws SAXException {
// Nothing to do
}
/**
* Signals the beginning of an Element node.
*
* @param uri The namespace URI
* @param name the local name of the element.
* @param qName the qualified naem of the element
* @param atts a list of attributes for this Element
* @throws org.xml.sax.SAXException If we are not given an element name.
*/
public void startElement(final String uri, final String name, final String qName, final Attributes atts) throws org.xml.sax.SAXException {
if (qName == null) {
throw new SAXException("No Element name given");
}
final String prefix;
final String localName;
int idx = qName.indexOf(':');
if (idx >= 0) {
prefix = qName.substring(0, idx);
localName = qName.substring(idx + 1);
} else {
prefix = "";
localName = qName;
}
Element element = new Element(null, localName);
if (_locator != null) {
element.setLocation(new Location(_locator));
}
_currentNode.addChild(element);
// Add all current namespaces to this element
for (Iterator i = _prefixes.entrySet().iterator(); i.hasNext(); ) {
Map.Entry me = (Map.Entry) i.next();
element.addNamespace(new Namespace((String)me.getKey(), (String) me.getValue()));
}
// Then add all attributes
if (atts != null && atts.getLength() > 0) {
for (int i = 0; i < atts.getLength(); i++) {
String attName = atts.getQName(i);
String ns = null;
idx = attName.indexOf(':');
if (idx > 0) {
ns = element.getNamespaceURI(attName.substring(0, idx));
attName = attName.substring(idx + 1);
}
element.addAttribute(new Attribute(ns, attName, atts.getValue(i)));
}
}
// Set the namespace on this Element, if one is explicit or defaulted
if (prefix != null && prefix.length() > 0) {
String namespace = element.getNamespaceURI(prefix);
element.setNamespace(namespace);
} else {
String namespace = element.getNamespaceURI("");
if (namespace != null) {
element.setNamespace(namespace);
}
}
_nodeStack.push(element);
_currentNode = element;
}
/**
* Begins the scope of a prefix-URI Namespace mapping.
* @param prefix The namespace prefix mapping that is ending
* @param uri The namespace URI
* @throws org.xml.sax.SAXException never
*/
public void startPrefixMapping(final String prefix, final String uri) {
_prefixes.put(prefix, uri);
}
}