package org.jdom.input; import java.io.*; import java.lang.reflect.*; import java.net.*; import java.util.*; import org.jdom.*; import org.xml.sax.*; import org.xml.sax.ext.LexicalHandler; import org.xml.sax.ext.DeclHandler; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; /** *
SAXHandler
supports SAXBuilder
Document
object being built */
private Document document;
/** Element stack */
private Stack stack;
/** Indicator of where in the document we are */
private boolean atRoot;
/** Indicator of whether we are in a DTD */
private boolean inDTD = false;
/** Indicator of whether we are in a CDATA */
private boolean inCDATA = false;
/** Indicator of whether we should expand entities */
private boolean expand = true;
/** Indicator of whether we are actively suppressing (non-expanding) a
current entity */
private boolean suppress = false;
/** How many nested entities we're currently within */
private int entityDepth = 0;
/** Temporary holder for namespaces that have been declared with
* startPrefixMapping, but are not yet available on the element */
private LinkedList declaredNamespaces;
/** The namespaces in scope and actually attached to an element */
private LinkedList availableNamespaces;
private Map externalEntities;
/** The JDOMFactory used for JDOM object creation */
private JDOMFactory factory;
/**
*
* This will set the Document
to use.
*
Document
being parsed.
* @throws IOException when errors occur.
*/
public SAXHandler(Document document) throws IOException {
this.document = document;
atRoot = true;
stack = new Stack();
declaredNamespaces = new LinkedList();
availableNamespaces = new LinkedList();
availableNamespaces.add(Namespace.XML_NAMESPACE);
externalEntities = new HashMap();
}
/**
*
* This will set the Document
to use
* and the factory for JDOM object creations
*
Document
being parsed.
* @param factory a JDOMFactory
implementation
* @throws IOException when errors occur.
*/
public SAXHandler(Document document, JDOMFactory factory) throws IOException {
this.document = document;
this.factory = factory;
atRoot = true;
stack = new Stack();
declaredNamespaces = new LinkedList();
availableNamespaces = new LinkedList();
availableNamespaces.add(Namespace.XML_NAMESPACE);
externalEntities = new HashMap();
}
// These methods from the DeclHandler interface we can ignore right now
public void attributeDecl(String eName, String aName, String type,
String valueDefault, String value) { }
/**
* * This will report character data (within an element). *
* * @param chchar[]
character array with character data
* @param start int
index in array where data starts.
* @param length int
length of data.
* @throws SAXException when things go wrong
*/
public void characters(char[] ch, int start, int length)
throws SAXException {
if (suppress) return;
String data = new String(ch, start, length);
/**
* This is commented out because of some problems with
* the inline DTDs that Xerces seems to have.
if (!inDTD) {
if (inEntity) {
((Entity)stack.peek()).setContent(data);
} else {
Element e = (Element)stack.peek();
e.addContent(data);
}
*/
if (inCDATA) {
((Element)stack.peek()).addContent(factory.cdata(data));
}
else {
Element e = (Element)stack.peek();
e.addContent(data);
}
}
/**
*
* This reports that a comments is parsed. If not in the
* DTD, this comment is added to the current JDOM
* Element
, or the Document
itself
* if at that level.
*
ch[]
array of comment characters.
* @param start int
index to start reading from.
* @param end int
index to end reading at.
*/
public void comment(char[] ch, int start, int end)
throws SAXException {
if (suppress) return;
String commentText = new String(ch, start, end);
if ((!inDTD) && (!commentText.equals(""))) {
if (stack.empty()) {
document.addContent(factory.comment(commentText));
} else {
((Element)stack.peek()).addContent(
factory.comment(commentText));
}
}
}
public void elementDecl(String name, String model) { }
/**
* * Report a CDATA section - ignored in SAXBuilder. *
*/ public void endCDATA() throws SAXException { if (suppress) return; inCDATA = false; } /** ** This signifies that the reading of the DTD is complete. *
*/ public void endDTD() throws SAXException { inDTD = false; } /** *
* Indicates the end of an element
* (</[element name]>
) is reached. Note that
* the parser does not distinguish between empty
* elements and non-empty elements, so this will occur uniformly.
*
String
URI of namespace this
* element is associated with
* @param localName String
name of element without prefix
* @param qName String
name of element in XML 1.0 form
* @throws SAXException when things go wrong
*/
public void endElement(String namespaceURI, String localName,
String qName) throws SAXException {
if (suppress) return;
Element element = (Element)stack.pop();
if (stack.empty()) {
atRoot = true;
}
// Remove the namespaces that this element makes available
List addl = element.getAdditionalNamespaces();
if (addl.size() > 0) {
availableNamespaces.removeAll(addl);
}
}
public void endEntity(String name) throws SAXException {
entityDepth--;
if (entityDepth == 0) {
// No way are we suppressing if not in an entity,
// regardless of the "expand" value
suppress = false;
}
}
/**
*
* This will add the prefix mapping to the JDOM
* Document
object.
*
String
namespace prefix.
* @param uri String
namespace URI.
*/
public void endPrefixMapping(String prefix)
throws SAXException {
if (suppress) return;
// Remove the namespace from the available list
// (Should find the namespace fast because recent adds
// are at the front of the list. It may not be the head
// tho because endPrefixMapping calls on the same element
// can come in any order.)
Iterator itr = availableNamespaces.iterator();
while (itr.hasNext()) {
Namespace ns = (Namespace) itr.next();
if (prefix.equals(ns.getPrefix())) {
itr.remove();
return;
}
}
}
/**
* This is called when the parser encounters an external entity
* declaration.
*
*
* @param name entity name
* @param publicId public id
* @param systemId system id
* @throws SAXException when things go wrong
*/
public void externalEntityDecl(String name,
String publicId, String systemId)
throws SAXException {
// Store the public and system ids for the name
externalEntities.put(name, new String[]{publicId, systemId});
}
/**
*
* For a given namespace prefix, this will return the
* {@link Namespace}
object for that prefix,
* within the current scope.
*
Namespace
- namespace for supplied prefix.
*/
private Namespace getNamespace(String prefix) {
Iterator i = availableNamespaces.iterator();
while (i.hasNext()) {
Namespace ns = (Namespace)i.next();
if (prefix.equals(ns.getPrefix())) {
return ns;
}
}
return Namespace.NO_NAMESPACE;
}
/**
* * Capture ignorable whitespace as text *
* * @param ch[]
- char array of ignorable whitespace
* @param start int
- starting position within array
* @param length int
- length of whitespace after start
* @throws SAXException when things go wrong
*/
public void ignorableWhitespace(char[] ch, int start,int length) throws SAXException {
if (suppress) return;
((Element)stack.peek()).addContent(new String(ch, start, length));
}
public void internalEntityDecl(String name, String value) { }
/**
* * This will indicate that a processing instruction (other than * the XML declaration) has been encountered. *
* * @param targetString
target of PI
* @param data String
* This sets whether or not to expand entities during the build.
* A true means to expand entities as normal content. A false means to
* leave entities unexpanded as EntityRef
objects. The
* default is true.
*
*
* @param expand boolean
indicating whether entity expansion
* should occur.
*/
public void setExpandEntities(boolean expand) {
this.expand = expand;
}
/**
* * Report a CDATA section - ignored in SAXBuilder. *
*/ public void startCDATA() throws SAXException { if (suppress) return; inCDATA = true; } /** *
* This will signify that a DTD is being parsed, and can be
* used to ensure that comments and other lexical structures
* in the DTD are not added to the JDOM Document
* object.
*
String
name of element listed in DTD
* @param publicId String
public ID of DTD
* @param systemId String
syste ID of DTD
*/
public void startDTD(String name, String publicId, String systemId)
throws SAXException {
document.setDocType(
factory.docType(name, publicId, systemId));
inDTD = true;
}
/**
*
* This reports the occurrence of an actual element. It will include
* the element's attributes, with the exception of XML vocabulary
* specific attributes, such as
* xmlns:[namespace prefix]
and
* xsi:schemaLocation
.
*
String
namespace URI this element
* is associated with, or an empty
* String
* @param localName String
name of element (with no
* namespace prefix, if one is present)
* @param qName String
XML 1.0 version of element name:
* [namespace prefix]:[localName]
* @param atts Attributes
list for this element
* @throws SAXException when things go wrong
*/
public void startElement(String namespaceURI, String localName,
String qName, Attributes atts)
throws SAXException {
if (suppress) return;
Element element = null;
if ((namespaceURI != null) && (!namespaceURI.equals(""))) {
String prefix = "";
// Determine any prefix on the Element
if (localName != qName) {
int split = qName.indexOf(":");
prefix = qName.substring(0, split);
}
Namespace elementNamespace =
Namespace.getNamespace(prefix, namespaceURI);
element = factory.element(localName, elementNamespace);
// Remove this namespace from those in the temp declared list
if (declaredNamespaces.size() > 0) {
declaredNamespaces.remove(elementNamespace);
}
// It's now in available scope
availableNamespaces.addFirst(elementNamespace);
} else {
element = factory.element(localName);
}
// Take leftover declared namespaces and add them to this element's
// map of namespaces
transferNamespaces(element);
// Handle attributes
for (int i=0, len=atts.getLength(); i
* This will add the prefix mapping to the JDOM
* Document
object.
*
String
namespace prefix.
* @param uri String
namespace URI.
*/
public void startPrefixMapping(String prefix, String uri)
throws SAXException {
if (suppress) return;
Namespace ns = Namespace.getNamespace(prefix, uri);
declaredNamespaces.add(ns);
}
/**
*
* This will take the supplied {@link Element}
and
* transfer its namespaces to the global namespace storage.
*
Element
to read namespaces from.
*/
private void transferNamespaces(Element element) {
Iterator i = declaredNamespaces.iterator();
while (i.hasNext()) {
Namespace ns = (Namespace)i.next();
i.remove();
availableNamespaces.addFirst(ns);
element.addNamespaceDeclaration(ns);
}
}
}