Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Issue #52 - SAXBuilder and memory.

Massive re-structure of SAXBuilder. Parser reuse is much more efficient,
and new mechanisms in place for XMLReader management.
Deprecated some methods on SAXBuilder, and added whole new package
org.jdom2.input.sax
Massive simplification of SAX parser configuration.
  • Loading branch information...
commit 751676c8e035b9e8fef3d128ee21e154bfc9d0e8 1 parent 4d3d404
@rolfl rolfl authored
Showing with 2,889 additions and 1,391 deletions.
  1. +2 −1  contrib/samples/LineNumberSAXBuilderDemo.java
  2. +1 −1  contrib/src/java/org/jdom2/contrib/ids/doc-files/TestIds.java
  3. +46 −42 contrib/src/java/org/jdom2/contrib/input/{LineNumberSAXBuilder.java → LineNumberSAXHandler.java}
  4. +605 −640 contrib/src/java/org/jdom2/contrib/input/scanner/ElementScanner.java
  5. +4 −17 contrib/src/java/org/jdom2/contrib/perf/PerfDoc.java
  6. +0 −173 core/src/java/org/jdom2/input/JAXPParserFactory.java
  7. +483 −366 core/src/java/org/jdom2/input/SAXBuilder.java
  8. +1 −1  core/src/java/org/jdom2/input/{ → sax}/BuilderErrorHandler.java
  9. +295 −0 core/src/java/org/jdom2/input/sax/SAXBuilderEngine.java
  10. +212 −0 core/src/java/org/jdom2/input/sax/SAXEngine.java
  11. +98 −94 core/src/java/org/jdom2/input/{ → sax}/SAXHandler.java
  12. +25 −0 core/src/java/org/jdom2/input/sax/SAXHandlerFactory.java
  13. +1 −1  core/src/java/org/jdom2/input/{ → sax}/TextBuffer.java
  14. +68 −0 core/src/java/org/jdom2/input/sax/XMLReaderJAXPSchemaFactory.java
  15. +111 −0 core/src/java/org/jdom2/input/sax/XMLReaderJAXPSingletons.java
  16. +36 −0 core/src/java/org/jdom2/input/sax/XMLReaderJDOMFactory.java
  17. +84 −0 core/src/java/org/jdom2/input/sax/XMLReaderSAX2Factory.java
  18. +249 −0 core/src/java/org/jdom2/input/sax/package-info.java
  19. +2 −1  core/src/java/org/jdom2/transform/JDOMResult.java
  20. +2 −1  test/src/java/org/jdom2/test/cases/input/TestBuilderErrorHandler.java
  21. +335 −48 test/src/java/org/jdom2/test/cases/input/TestSAXBuilder.java
  22. +1 −1  test/src/java/org/jdom2/test/cases/input/TestSAXHandler.java
  23. +77 −0 test/src/java/org/jdom2/test/cases/input/sax/TestJAXPXMLReaderSingleton.java
  24. +91 −0 test/src/java/org/jdom2/test/cases/input/sax/TestSAX2XMLReaderFactory.java
  25. +56 −0 test/src/java/org/jdom2/test/cases/input/sax/TestSchemaXMLReaderFactory.java
  26. +1 −1  test/src/java/org/jdom2/test/cases/output/TestSAXOutputter.java
  27. +1 −1  test/src/java/org/jdom2/test/cases/output/TestStAXEventOutputter.java
  28. +2 −2 test/src/java/org/jdom2/test/cases/special/TestIssue008ExpandEntity.java
View
3  contrib/samples/LineNumberSAXBuilderDemo.java
@@ -58,7 +58,8 @@ JDOM Project Management (pm@jdom.org).
{
public static void main(String[] args) throws Exception {
- SAXBuilder builder = new LineNumberSAXBuilder();
+ SAXBuilder builder = new SAXBuilder();
+ builder.setSAXHandlerFactory(LineNumberSAXHandler.SAXFACTORY);
Document doc = builder.build(new StringReader(xml));
for (Iterator<LineNumberElement> iter = doc.getDescendants(Filters.fclass(LineNumberElement.class));
View
2  contrib/src/java/org/jdom2/contrib/ids/doc-files/TestIds.java
@@ -18,7 +18,7 @@ public static void main(String[] args) throws Exception {
}
SAXBuilder builder = new SAXBuilder();
- builder.setFactory(new IdFactory());
+ builder.setJDOMFactory(new IdFactory());
IdDocument doc = (IdDocument)(builder.build(args[0]));
Element elt = doc.getElementById(args[1]);
View
88 ...2/contrib/input/LineNumberSAXBuilder.java → ...2/contrib/input/LineNumberSAXHandler.java
@@ -52,18 +52,19 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
*/
- package org.jdom2.contrib.input;
+package org.jdom2.contrib.input;
-import org.jdom2.Element;
-import org.jdom2.Namespace;
-import org.jdom2.DefaultJDOMFactory;
-import org.jdom2.JDOMFactory;
-import org.jdom2.input.SAXBuilder;
-import org.jdom2.input.SAXHandler;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
+import org.jdom2.DefaultJDOMFactory;
+import org.jdom2.Element;
+import org.jdom2.JDOMFactory;
+import org.jdom2.Namespace;
+import org.jdom2.input.sax.SAXHandler;
+import org.jdom2.input.sax.SAXHandlerFactory;
+
/**
* This builder works in parallell with {@link LineNumberElement}
* to provide each element with information on its beginning and
@@ -80,16 +81,21 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* @author Per Norrman
*
*/
-public class LineNumberSAXBuilder extends SAXBuilder
-{
- @Override
- protected SAXHandler createContentHandler()
- {
- return new MySAXHandler(new MyFactory());
- }
+public class LineNumberSAXHandler extends SAXHandler {
- private class MyFactory extends DefaultJDOMFactory
- {
+ /**
+ * A SAXHandlerFactory that can be used to supply LineNumberSAXHandler
+ * instances to SAXBuilder.
+ */
+ public static final SAXHandlerFactory SAXFACTORY = new SAXHandlerFactory() {
+ @Override
+ public SAXHandler createSAXHandler(JDOMFactory factory) {
+ // ignore input factory, we use our own.
+ return new LineNumberSAXHandler();
+ }
+ };
+
+ private static class MyJDOMFactory extends DefaultJDOMFactory {
@Override
public Element element(String name)
@@ -108,7 +114,7 @@ public Element element(String name, Namespace namespace)
{
return new LineNumberElement(name, namespace);
}
-
+
@Override
public Element element(String name, String uri)
{
@@ -117,47 +123,45 @@ public Element element(String name, String uri)
}
- private class MySAXHandler extends SAXHandler
+ /**
+ * Create a new instance of the LineNumberSAXHandler.
+ */
+ public LineNumberSAXHandler()
{
+ super(new MyJDOMFactory());
+ }
- public MySAXHandler(JDOMFactory f)
- {
- super(f);
- }
-
- /** override */
- @Override
- public void startElement(
+ /** override */
+ @Override
+ public void startElement(
String arg0,
String arg1,
String arg2,
Attributes arg3)
throws SAXException
+ {
+ super.startElement(arg0, arg1, arg2, arg3);
+ Locator l = getDocumentLocator();
+ if (l != null)
{
- super.startElement(arg0, arg1, arg2, arg3);
- Locator l = getDocumentLocator();
- if (l != null)
- {
- ((LineNumberElement) getCurrentElement()).setStartLine(
+ ((LineNumberElement) getCurrentElement()).setStartLine(
l.getLineNumber());
- }
}
+ }
- /** override */
- @Override
- public void endElement(String arg0, String arg1, String arg2)
+ /** override */
+ @Override
+ public void endElement(String arg0, String arg1, String arg2)
throws SAXException
+ {
+ Locator l = getDocumentLocator();
+ if (l != null)
{
- Locator l = getDocumentLocator();
- if (l != null)
- {
- ((LineNumberElement) getCurrentElement()).setEndLine(
+ ((LineNumberElement) getCurrentElement()).setEndLine(
l.getLineNumber());
- }
-
- super.endElement(arg0, arg1, arg2);
}
+ super.endElement(arg0, arg1, arg2);
}
}
View
1,245 contrib/src/java/org/jdom2/contrib/input/scanner/ElementScanner.java
@@ -68,7 +68,10 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
import org.jdom2.*;
import org.jdom2.input.SAXBuilder;
-import org.jdom2.input.SAXHandler;
+import org.jdom2.input.sax.XMLReaderJAXPSingletons;
+import org.jdom2.input.sax.XMLReaderJDOMFactory;
+import org.jdom2.input.sax.SAXHandler;
+import org.jdom2.input.sax.SAXHandlerFactory;
/**
* An XML filter that uses XPath-like expressions to select the
@@ -163,645 +166,607 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
@SuppressWarnings("javadoc")
public class ElementScanner extends XMLFilterImpl {
- /**
- * The registered element listeners, each wrapped in a
- * XPathMatcher instance.
- */
- private final Collection<XPathMatcher> listeners = new ArrayList<XPathMatcher>();
-
- /**
- * The <i>SAXBuilder</i> instance to build the JDOM objects used
- * for parsing the input XML documents. We actually do not need
- * SAXBuilder per se, we just want to reuse the tons of Java code
- * this class implements!
- */
- private ParserBuilder parserBuilder = new ParserBuilder();
-
- /**
- * The <i>SAXHandler</i> instance to build the JDOM Elements.
- */
- private SAXHandler saxHandler = null;
-
- /**
- * The path of the being parsed element.
- */
- private StringBuffer currentPath = new StringBuffer();
-
- /**
- * The matching rules active for the current path. It includes
- * the matching rules active for all the ancestors of the
- * current node.
- */
- private Map<String,Collection<XPathMatcher>> activeRules = new HashMap<String, Collection<XPathMatcher>>();
-
- /**
- * Construct an ElementScanner, with no parent.
- * <p>
- * If no parent has been assigned when {@link #parse} is invoked,
- * ElementScanner will use JAXP to get an instance of the default
- * SAX parser installed.</p>
- */
- public ElementScanner() {
- super();
- }
-
- /**
- * Constructs an ElementScanner with the specified parent.
- */
- public ElementScanner(XMLReader parent) {
- super(parent);
- }
-
- //-------------------------------------------------------------------------
- // Specific implementation
- //-------------------------------------------------------------------------
-
- /**
- * Adds a new element listener to the list of listeners
- * maintained by this filter.
- * <p>
- * The same listener can be registered several times using
- * different patterns and several listeners can be registered
- * using the same pattern.</p>
- *
- * @param listener the element listener to add.
- * @param pattern the XPath expression to select the elements
- * the listener is interested in.
- *
- * @throws JDOMException if <code>listener</code> is null or
- * the expression is invalid.
- */
- public void addElementListener(ElementListener listener, String pattern)
- throws JDOMException {
- if (listener != null) {
- this.listeners.add(XPathMatcher.newXPathMatcher(pattern, listener));
- }
- else {
- throw (new JDOMException("Invalid listener object: <null>"));
- }
- }
-
- /**
- * Removes element listeners from the list of listeners maintained
- * by this filter.
- * <p>
- * if <code>pattern</code> is <code>null</code>, this method
- * removes all registrations of <code>listener</code>, regardless
- * the pattern(s) used for creating the registrations.</p>
- * <p>
- * if <code>listener</code> is <code>null</code>, this method
- * removes all listeners registered for <code>pattern</code>.</p>
- * <p>
- * if both <code>listener</code> and <code>pattern</code> are
- * <code>null</code>, this method performs no action!</p>
- *
- * @param listener the element listener to remove.
- */
- public void removeElementListener(ElementListener listener, String pattern) {
- if ((listener != null) || (pattern != null)) {
- for (Iterator<XPathMatcher> i=this.listeners.iterator(); i.hasNext(); ) {
- XPathMatcher m = i.next();
-
- if (((m.getListener().equals(listener)) || (listener == null)) &&
- ((m.getExpression().equals(pattern)) || (pattern == null))) {
- i.remove();
- }
- }
- }
- // Else: Both null => Just ignore that dummy call!
- }
-
- /**
- * Returns the list of rules that match the element path and
- * attributes.
- *
- * @param path the current element path.
- * @param attrs the attributes of the element.
- *
- * @return the list of matching rules or <code>null</code> if
- * no match was found.
- */
- private Collection<XPathMatcher> getMatchingRules(String path, Attributes attrs) {
- Collection<XPathMatcher> matchingRules = null;
-
- for (XPathMatcher rule : this.listeners) {
- if (rule.match(path, attrs)) {
- if (matchingRules == null) {
- matchingRules = new ArrayList<XPathMatcher>();
- }
- matchingRules.add(rule);
- }
- }
- return (matchingRules);
- }
-
- //-------------------------------------------------------------------------
- // SAXBuilder / SAXHandler configuration helper methods
- //-------------------------------------------------------------------------
-
- /**
- * Sets a custom JDOMFactory for the builder. Use this to build
- * the tree with your own subclasses of the JDOM classes.
- *
- * @param factory <code>JDOMFactory</code> to use.
- */
- public void setFactory(JDOMFactory factory) {
- this.parserBuilder.setFactory(factory);
- }
-
- /**
- * Activates or desactivates validation for the builder.
- *
- * @param validate whether XML validation should occur.
- */
- public void setValidation(boolean validate) {
- this.parserBuilder.setValidation(validate);
- }
-
- /**
- * Specifies whether or not the parser should elminate whitespace
- * in element content (sometimes known as "ignorable whitespace")
- * when building the document. Only whitespace which is contained
- * within element content that has an element only content model
- * will be eliminated (see XML Rec 3.2.1). For this setting to
- * take effect requires that validation be turned on.
- * <p>
- * The default value is <code>false</code>.</p>
- *
- * @param ignoringWhite whether to ignore ignorable whitespace.
- */
- public void setIgnoringElementContentWhitespace(boolean ignoringWhite) {
- this.parserBuilder.setIgnoringElementContentWhitespace(ignoringWhite);
- }
-
- /**
- * Sets whether or not to expand entities for the builder.
- * <p>
- * A value <code>true</code> means to expand entities as normal
- * content; <code>false</code> means to leave entities unexpanded
- * as <code>EntityRef</code> objects.</p>
- * <p>
- * The default value is <code>true</code>.</p>
- *
- * @param expand whether entity expansion should occur.
- */
- public void setExpandEntities(boolean expand) {
- this.parserBuilder.setExpandEntities(expand);
- }
-
- //-------------------------------------------------------------------------
- // XMLFilterImpl overwritten methods
- //-------------------------------------------------------------------------
-
- //-------------------------------------------------------------------------
- // XMLReader interface support
- //-------------------------------------------------------------------------
-
- /**
- * Sets the state of a feature.
- *
- * @param name the feature name, which is a fully-qualified
- * URI.
- * @param state the requested state of the feature.
- *
- * @throws SAXNotRecognizedException when the XMLReader does not
- * recognize the feature name.
- * @throws SAXNotSupportedException when the XMLReader
- * recognizes the feature name but cannot set the
- * requested value.
- */
- @Override
-public void setFeature(String name, boolean state)
- throws SAXNotRecognizedException, SAXNotSupportedException {
- if (this.getParent() != null) {
- this.getParent().setFeature(name, state);
- }
- this.parserBuilder.setFeature(name, state);
- }
-
- /**
- * Set the value of a property.
- *
- * @param name the property name, which is a fully-qualified
- * URI.
- * @param value the requested value for the property.
- *
- * @throws SAXNotRecognizedException when the XMLReader does not
- * recognize the property name.
- * @throws SAXNotSupportedException when the XMLReader
- * recognizes the property name but cannot set the
- * requested value.
- */
- @Override
-public void setProperty(String name, Object value)
- throws SAXNotRecognizedException, SAXNotSupportedException {
- if (this.getParent() != null) {
- this.getParent().setProperty(name, value);
- }
- this.parserBuilder.setProperty(name, value);
- }
-
- /**
- * Parses an XML document.
- * <p>
- * The application can use this method to instruct ElementScanner
- * to begin parsing an XML document from any valid input source
- * (a character stream, a byte stream, or a URI).</p>
- * <p>
- * Applications may not invoke this method while a parse is in
- * progress. Once a parse is complete, an application may reuse
- * the same ElementScanner object, possibly with a different input
- * source.</p>
- * <p>
- * This method is synchronous: it will not return until parsing
- * has ended. If a client application wants to terminate parsing
- * early, it should throw an exception.</p>
- *
- * @param source the input source for the XML document.
- *
- * @throws SAXException any SAX exception, possibly wrapping
- * another exception.
- * @throws IOException an IO exception from the parser,
- * possibly from a byte stream or character
- * stream supplied by the application.
- */
- @Override
-public void parse(InputSource source) throws IOException, SAXException {
- // Allocate the element builder (SAXHandler subclass).
- this.saxHandler = this.parserBuilder.getContentHandler();
-
- // Allocate (if not provided) and configure the parent parser.
- this.setParent(this.parserBuilder.getXMLReader(
- this.getParent(), this.saxHandler));
-
- // And delegate to superclass now that everything has been set-up.
- // Note: super.parse() forces the registration of this filter as
- // ContentHandler, ErrorHandler, DTDHandler and EntityResolver.
- super.parse(source);
- }
-
- //-------------------------------------------------------------------------
- // ContentHandler interface support
- //-------------------------------------------------------------------------
-
- /**
- * <i>[ContentHandler interface support]</i> Receives notification
- * of the beginning of a document.
- *
- * @throws SAXException any SAX exception, possibly wrapping
- * another exception.
- */
- @Override
-public void startDocument() throws SAXException {
- // Reset state.
- this.currentPath.setLength(0);
- this.activeRules.clear();
-
- // Propagate event.
- this.saxHandler.startDocument();
- super.startDocument();
- }
-
- /**
- * <i>[ContentHandler interface support]</i> Receives notification
- * of the end of a document.
- *
- * @throws SAXException any SAX exception, possibly wrapping
- * another exception.
- */
- @Override
-public void endDocument() throws SAXException {
- // Propagate event.
- this.saxHandler.endDocument();
- super.endDocument();
- }
-
- /**
- * <i>[ContentHandler interface support]</i> Begins the scope of
- * a prefix-URI Namespace mapping.
- *
- * @param prefix the Namespace prefix being declared.
- * @param uri the Namespace URI the prefix is mapped to.
- *
- * @throws SAXException any SAX exception, possibly wrapping
- * another exception.
- */
- @Override
-public void startPrefixMapping(String prefix, String uri)
- throws SAXException {
- // Propagate event.
- this.saxHandler.startPrefixMapping(prefix, uri);
- super.startPrefixMapping(prefix, uri);
- }
-
- /**
- * <i>[ContentHandler interface support]</i> Ends the scope of a
- * prefix-URI Namespace mapping.
- *
- * @param prefix the prefix that was being mapped.
- *
- * @throws SAXException any SAX exception, possibly wrapping
- * another exception.
- */
- @Override
-public void endPrefixMapping(String prefix) throws SAXException {
- // Propagate event.
- this.saxHandler.endPrefixMapping(prefix);
- super.endPrefixMapping(prefix);
- }
-
- /**
- * <i>[ContentHandler interface support]</i> Receives notification
- * of the beginning of an element.
- *
- * @param nsUri the Namespace URI, or the empty string if
- * the element has no Namespace URI or if
- * Namespace processing is not being performed.
- * @param localName the local name (without prefix), or the
- * empty string if Namespace processing is
- * not being performed.
- * @param qName the qualified name (with prefix), or the
- * empty string if qualified names are not
- * available.
- * @param attrs the attributes attached to the element. If
- * there are no attributes, it shall be an
- * empty Attributes object.
- *
- * @throws SAXException any SAX exception, possibly wrapping
- * another exception.
- */
- @Override
-public void startElement(String nsUri, String localName,
- String qName, Attributes attrs)
- throws SAXException {
- // Append new element to the current path.
- this.currentPath.append('/').append(localName);
-
- // Retrieve the matching rules for this element.
- String eltPath = this.currentPath.substring(0);
- Collection<XPathMatcher> matchingRules = this.getMatchingRules(eltPath, attrs);
- if (matchingRules != null) {
- // Matching rules found.
- // => Make them active to trigger element building.
- this.activeRules.put(eltPath, matchingRules);
- }
-
- // Propagate event.
- if (this.activeRules.size() != 0) {
- this.saxHandler.startElement(nsUri, localName, qName, attrs);
- }
- super.startElement(nsUri, localName, qName, attrs);
- }
-
- /**
- * <i>[ContentHandler interface support]</i> Receives notification
- * of the end of an element.
- *
- * @param nsUri the Namespace URI, or the empty string if
- * the element has no Namespace URI or if
- * Namespace processing is not being performed.
- * @param localName the local name (without prefix), or the
- * empty string if Namespace processing is
- * not being performed.
- * @param qName the qualified name (with prefix), or the
- * empty string if qualified names are not
- * available.
- *
- * @throws SAXException any SAX exception, possibly wrapping
- * another exception.
- */
- @Override
-public void endElement(String nsUri, String localName, String qName)
- throws SAXException {
- // Grab the being-built element.
- Element elt = this.saxHandler.getCurrentElement();
-
- // Complete element building before making use of it.
- // (This sets the current element to the parent of elt.)
- if (this.activeRules.size() != 0) {
- this.saxHandler.endElement(nsUri, localName, qName);
- }
-
- // Get the matching rules for this element (if any).
- String eltPath = this.currentPath.substring(0);
- Collection<XPathMatcher> matchingRules = this.activeRules.remove(eltPath);
- if (matchingRules != null) {
- // Matching rules found.
- // => Detach the current element if no rules remain active.
- if (this.activeRules.size() == 0) {
- elt.detach();
- }
-
- // And notify all matching listeners.
- try {
- for (XPathMatcher matcher : matchingRules) {
- if (matcher.match(eltPath, elt)) {
- matcher.getListener().elementMatched(eltPath, elt);
- }
- }
- }
- catch (JDOMException ex1) {
- // Oops! Listener-originated exception.
- // => Fire a SAXException to abort parsing.
- throw (new SAXException(ex1.getMessage(), ex1));
- }
- }
- // Remove notified element from the current path.
- this.currentPath.setLength(
- this.currentPath.length() - (localName.length() + 1));
- // Propagate event.
- super.endElement(nsUri, localName, qName);
- }
-
- /**
- * <i>[ContentHandler interface support]</i> Receives notification
- * of character data.
- *
- * @param ch the characters from the XML document.
- * @param start the start position in the array.
- * @param length the number of characters to read from the array.
- *
- * @throws SAXException any SAX exception, possibly wrapping
- * another exception.
- */
- @Override
-public void characters(char[] ch, int start, int length)
- throws SAXException {
- // Propagate event.
- if (this.activeRules.size() != 0) {
- this.saxHandler.characters(ch, start, length);
- }
- super.characters(ch, start, length);
- }
-
- /**
- * <i>[ContentHandler interface support]</i> Receives notification
- * of ignorable whitespace in element content.
- *
- * @param ch the characters from the XML document.
- * @param start the start position in the array.
- * @param length the number of characters to read from the array.
- *
- * @throws SAXException any SAX exception, possibly wrapping
- * another exception.
- */
- @Override
-public void ignorableWhitespace(char[] ch, int start, int length)
- throws SAXException {
- // Propagate event.
- if (this.activeRules.size() != 0) {
- this.saxHandler.ignorableWhitespace(ch, start, length);
- }
- super.ignorableWhitespace(ch, start, length);
- }
-
- /**
- * <i>[ContentHandler interface support]</i> Receives notification
- * of processing instruction.
- *
- * @param target the processing instruction target.
- * @param data the processing instruction data, or
- * <code>null</code> if none was supplied.
- *
- * @throws SAXException any SAX exception, possibly wrapping
- * another exception.
- */
- @Override
-public void processingInstruction(String target, String data)
- throws SAXException {
- // Propagate event.
- if (this.activeRules.size() != 0) {
- this.saxHandler.processingInstruction(target, data);
- }
- super.processingInstruction(target, data);
- }
-
- /**
- * <i>[ContentHandler interface support]</i> Receives notification
- * of a skipped entity.
- *
- * @param name the name of the skipped entity.
- *
- * @throws SAXException any SAX exception, possibly wrapping
- * another exception.
- */
- @Override
-public void skippedEntity(String name) throws SAXException {
- // Propagate event.
- if (this.activeRules.size() != 0) {
- this.saxHandler.skippedEntity(name);
- }
- super.skippedEntity(name);
- }
-
-
- //=========================================================================
- // Deviant implementations of JDOM builder objects
- //=========================================================================
-
- //-------------------------------------------------------------------------
- // ParserBuilder nested class
- //-------------------------------------------------------------------------
-
- /**
- * ParserBuilder extends SAXBuilder to provide access to the
- * protected methods of this latter and to allow us using
- * customized SAXHandler and JDOMFactory implementations.
- */
- private static class ParserBuilder extends SAXBuilder {
-
- public ParserBuilder() {
- super();
- }
-
- //----------------------------------------------------------------------
- // SAXBuilder overwritten methods
- //----------------------------------------------------------------------
-
- @Override
- protected SAXHandler createContentHandler() {
- return (new FragmentHandler(getFactory()));
- }
-
- //----------------------------------------------------------------------
- // Specific implementation
- //----------------------------------------------------------------------
-
- /**
- * Allocates and configures a new XMLReader instance. If a
- * parser is provided, it will be used; otherwise a new parser
- * will be created.
- *
- * @param parser the parser to configure, <code>null</code>
- * if one shall be allocated.
- * @param handler the SAX ContentHandler to register on the
- * parser.
- *
- * @return the configured parser.
- *
- * @throws SAXException any SAX exception, possibly wrapping
- * another exception.
- */
- public XMLReader getXMLReader(XMLReader parser, SAXHandler handler)
- throws SAXException {
- try {
- // Allocate a SAX parser if none was provided.
- if (parser == null) {
- parser = this.createParser();
- }
- // And configure the parser with the ContentHandler.
- this.configureParser(parser, handler);
-
- return (parser);
- }
- catch (Exception ex1) {
- throw (new SAXException(ex1.getMessage(), ex1));
- }
- }
-
- /**
- * Allocates and configures a new SAXHandler object.
- *
- * @return the configured SAXHandler.
- *
- * @throws SAXException any SAX exception, possibly wrapping
- * another exception.
- */
- public SAXHandler getContentHandler() throws SAXException {
- try {
- SAXHandler handler = this.createContentHandler();
- this.configureContentHandler(handler);
-
- return (handler);
- }
- catch (Exception ex1) {
- throw (new SAXException(ex1.getMessage(), ex1));
- }
- }
- }
-
- //-------------------------------------------------------------------------
- // FragmentHandler nested class
- //-------------------------------------------------------------------------
-
- /**
- * FragmentHandler extends SAXHandler to support matching nodes
- * without a common ancestor. This class inserts a dummy root
- * element in the being-built document. This prevents the document
- * to have, from SAXHandler's point of view, multiple root
- * elements (which would cause the parse to fail).
- */
- private static class FragmentHandler extends SAXHandler {
- /**
- * Public constructor.
- */
- public FragmentHandler(JDOMFactory factory) {
- super(factory);
-
- // Add a dummy root element to the being-built document.
- this.pushElement(new Element("root", null, null));
- }
- }
+ /**
+ * The registered element listeners, each wrapped in a
+ * XPathMatcher instance.
+ */
+ private final Collection<XPathMatcher> listeners = new ArrayList<XPathMatcher>();
+
+ /**
+ * The <i>SAXBuilder</i> instance to build the JDOM objects used
+ * for parsing the input XML documents. We actually do not need
+ * SAXBuilder per se, we just want to reuse the tons of Java code
+ * this class implements!
+ */
+ private SAXBuilder parserBuilder = new SAXBuilder();
+
+ /**
+ * The <i>SAXHandler</i> instance to build the JDOM Elements.
+ */
+ private SAXHandler saxHandler = null;
+
+ /**
+ * The path of the being parsed element.
+ */
+ private StringBuffer currentPath = new StringBuffer();
+
+ /**
+ * The matching rules active for the current path. It includes
+ * the matching rules active for all the ancestors of the
+ * current node.
+ */
+ private Map<String,Collection<XPathMatcher>> activeRules = new HashMap<String, Collection<XPathMatcher>>();
+
+ /**
+ * Construct an ElementScanner, with no parent.
+ * <p>
+ * If no parent has been assigned when {@link #parse} is invoked,
+ * ElementScanner will use JAXP to get an instance of the default
+ * SAX parser installed.</p>
+ */
+ public ElementScanner() {
+ super();
+ }
+
+ /**
+ * Constructs an ElementScanner with the specified parent.
+ */
+ public ElementScanner(XMLReader parent) {
+ super(parent);
+ }
+
+ //-------------------------------------------------------------------------
+ // Specific implementation
+ //-------------------------------------------------------------------------
+
+ /**
+ * Adds a new element listener to the list of listeners
+ * maintained by this filter.
+ * <p>
+ * The same listener can be registered several times using
+ * different patterns and several listeners can be registered
+ * using the same pattern.</p>
+ *
+ * @param listener the element listener to add.
+ * @param pattern the XPath expression to select the elements
+ * the listener is interested in.
+ *
+ * @throws JDOMException if <code>listener</code> is null or
+ * the expression is invalid.
+ */
+ public void addElementListener(ElementListener listener, String pattern)
+ throws JDOMException {
+ if (listener != null) {
+ this.listeners.add(XPathMatcher.newXPathMatcher(pattern, listener));
+ }
+ else {
+ throw (new JDOMException("Invalid listener object: <null>"));
+ }
+ }
+
+ /**
+ * Removes element listeners from the list of listeners maintained
+ * by this filter.
+ * <p>
+ * if <code>pattern</code> is <code>null</code>, this method
+ * removes all registrations of <code>listener</code>, regardless
+ * the pattern(s) used for creating the registrations.</p>
+ * <p>
+ * if <code>listener</code> is <code>null</code>, this method
+ * removes all listeners registered for <code>pattern</code>.</p>
+ * <p>
+ * if both <code>listener</code> and <code>pattern</code> are
+ * <code>null</code>, this method performs no action!</p>
+ *
+ * @param listener the element listener to remove.
+ */
+ public void removeElementListener(ElementListener listener, String pattern) {
+ if ((listener != null) || (pattern != null)) {
+ for (Iterator<XPathMatcher> i=this.listeners.iterator(); i.hasNext(); ) {
+ XPathMatcher m = i.next();
+
+ if (((m.getListener().equals(listener)) || (listener == null)) &&
+ ((m.getExpression().equals(pattern)) || (pattern == null))) {
+ i.remove();
+ }
+ }
+ }
+ // Else: Both null => Just ignore that dummy call!
+ }
+
+ /**
+ * Returns the list of rules that match the element path and
+ * attributes.
+ *
+ * @param path the current element path.
+ * @param attrs the attributes of the element.
+ *
+ * @return the list of matching rules or <code>null</code> if
+ * no match was found.
+ */
+ private Collection<XPathMatcher> getMatchingRules(String path, Attributes attrs) {
+ Collection<XPathMatcher> matchingRules = null;
+
+ for (XPathMatcher rule : this.listeners) {
+ if (rule.match(path, attrs)) {
+ if (matchingRules == null) {
+ matchingRules = new ArrayList<XPathMatcher>();
+ }
+ matchingRules.add(rule);
+ }
+ }
+ return (matchingRules);
+ }
+
+ //-------------------------------------------------------------------------
+ // SAXBuilder / SAXHandler configuration helper methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Sets a custom JDOMFactory for the builder. Use this to build
+ * the tree with your own subclasses of the JDOM classes.
+ *
+ * @param factory <code>JDOMFactory</code> to use.
+ */
+ public void setFactory(JDOMFactory factory) {
+ this.parserBuilder.setJDOMFactory(factory);
+ }
+
+ /**
+ * Activates or deactivates validation for the builder.
+ *
+ * @param validate whether XML validation should occur.
+ */
+ public void setValidation(boolean validate) {
+ if (validate)
+ this.parserBuilder.setXMLReaderFactory(XMLReaderJAXPSingletons.DTDVALIDATING);
+ else
+ this.parserBuilder.setXMLReaderFactory(XMLReaderJAXPSingletons.NONVALIDATING);
+ }
+
+ /**
+ * Specifies whether or not the parser should elminate whitespace
+ * in element content (sometimes known as "ignorable whitespace")
+ * when building the document. Only whitespace which is contained
+ * within element content that has an element only content model
+ * will be eliminated (see XML Rec 3.2.1). For this setting to
+ * take effect requires that validation be turned on.
+ * <p>
+ * The default value is <code>false</code>.</p>
+ *
+ * @param ignoringWhite whether to ignore ignorable whitespace.
+ */
+ public void setIgnoringElementContentWhitespace(boolean ignoringWhite) {
+ this.parserBuilder.setIgnoringElementContentWhitespace(ignoringWhite);
+ }
+
+ /**
+ * Sets whether or not to expand entities for the builder.
+ * <p>
+ * A value <code>true</code> means to expand entities as normal
+ * content; <code>false</code> means to leave entities unexpanded
+ * as <code>EntityRef</code> objects.</p>
+ * <p>
+ * The default value is <code>true</code>.</p>
+ *
+ * @param expand whether entity expansion should occur.
+ */
+ public void setExpandEntities(boolean expand) {
+ this.parserBuilder.setExpandEntities(expand);
+ }
+
+ //-------------------------------------------------------------------------
+ // XMLFilterImpl overwritten methods
+ //-------------------------------------------------------------------------
+
+ //-------------------------------------------------------------------------
+ // XMLReader interface support
+ //-------------------------------------------------------------------------
+
+ /**
+ * Sets the state of a feature.
+ *
+ * @param name the feature name, which is a fully-qualified
+ * URI.
+ * @param state the requested state of the feature.
+ *
+ * @throws SAXNotRecognizedException when the XMLReader does not
+ * recognize the feature name.
+ * @throws SAXNotSupportedException when the XMLReader
+ * recognizes the feature name but cannot set the
+ * requested value.
+ */
+ @Override
+ public void setFeature(String name, boolean state)
+ throws SAXNotRecognizedException, SAXNotSupportedException {
+ if (this.getParent() != null) {
+ this.getParent().setFeature(name, state);
+ }
+ this.parserBuilder.setFeature(name, state);
+ }
+
+ /**
+ * Set the value of a property.
+ *
+ * @param name the property name, which is a fully-qualified
+ * URI.
+ * @param value the requested value for the property.
+ *
+ * @throws SAXNotRecognizedException when the XMLReader does not
+ * recognize the property name.
+ * @throws SAXNotSupportedException when the XMLReader
+ * recognizes the property name but cannot set the
+ * requested value.
+ */
+ @Override
+ public void setProperty(String name, Object value)
+ throws SAXNotRecognizedException, SAXNotSupportedException {
+ if (this.getParent() != null) {
+ this.getParent().setProperty(name, value);
+ }
+ this.parserBuilder.setProperty(name, value);
+ }
+
+ /**
+ * Parses an XML document.
+ * <p>
+ * The application can use this method to instruct ElementScanner
+ * to begin parsing an XML document from any valid input source
+ * (a character stream, a byte stream, or a URI).</p>
+ * <p>
+ * Applications may not invoke this method while a parse is in
+ * progress. Once a parse is complete, an application may reuse
+ * the same ElementScanner object, possibly with a different input
+ * source.</p>
+ * <p>
+ * This method is synchronous: it will not return until parsing
+ * has ended. If a client application wants to terminate parsing
+ * early, it should throw an exception.</p>
+ *
+ * @param source the input source for the XML document.
+ *
+ * @throws SAXException any SAX exception, possibly wrapping
+ * another exception.
+ * @throws IOException an IO exception from the parser,
+ * possibly from a byte stream or character
+ * stream supplied by the application.
+ */
+ @Override
+ public void parse(InputSource source) throws IOException, SAXException {
+ final SAXHandler shandler = new FragmentHandler(parserBuilder.getJDOMFactory());
+ SAXHandlerFactory shfactory = new SAXHandlerFactory() {
+ @Override
+ public SAXHandler createSAXHandler(JDOMFactory fac) {
+ // ignore the fac.
+ return shandler;
+ }
+ };
+ final XMLReaderJDOMFactory currentfac = parserBuilder.getXMLReaderFactory();
+ final SAXHandlerFactory currentshfac = parserBuilder.getSAXHandlerFactory();
+ try {
+ final XMLReader xreader = getParent() != null
+ ? getParent()
+ : parserBuilder.getXMLReaderFactory().createXMLReader();
+ final boolean validating = currentfac.isValidating();
+ parserBuilder.setSAXHandlerFactory(shfactory);
+ parserBuilder.setXMLReaderFactory(new XMLReaderJDOMFactory() {
+ @Override
+ public boolean isValidating() {
+ return validating;
+ }
+
+ @Override
+ public XMLReader createXMLReader() throws JDOMException {
+ return xreader;
+ }
+ });
+ // configures the sax handler and parser to mate.
+ parserBuilder.buildEngine();
+ // Allocate the element builder (SAXHandler subclass).
+ this.saxHandler = shandler;
+
+ // Allocate (if not provided) and configure the parent parser.
+ if (this.getParent() != null) {
+ setParent(xreader);
+ }
+
+ } catch (JDOMException e) {
+ throw new SAXException("Problem in JDOM.", e);
+ } finally {
+ parserBuilder.setXMLReaderFactory(currentfac);
+ parserBuilder.setSAXHandlerFactory(currentshfac);
+ }
+ // And delegate to superclass now that everything has been set-up.
+ // Note: super.parse() forces the registration of this filter as
+ // ContentHandler, ErrorHandler, DTDHandler and EntityResolver.
+ super.parse(source);
+ }
+
+ //-------------------------------------------------------------------------
+ // ContentHandler interface support
+ //-------------------------------------------------------------------------
+
+ /**
+ * <i>[ContentHandler interface support]</i> Receives notification
+ * of the beginning of a document.
+ *
+ * @throws SAXException any SAX exception, possibly wrapping
+ * another exception.
+ */
+ @Override
+ public void startDocument() throws SAXException {
+ // Reset state.
+ this.currentPath.setLength(0);
+ this.activeRules.clear();
+
+ // Propagate event.
+ this.saxHandler.startDocument();
+ super.startDocument();
+ }
+
+ /**
+ * <i>[ContentHandler interface support]</i> Receives notification
+ * of the end of a document.
+ *
+ * @throws SAXException any SAX exception, possibly wrapping
+ * another exception.
+ */
+ @Override
+ public void endDocument() throws SAXException {
+ // Propagate event.
+ this.saxHandler.endDocument();
+ super.endDocument();
+ }
+
+ /**
+ * <i>[ContentHandler interface support]</i> Begins the scope of
+ * a prefix-URI Namespace mapping.
+ *
+ * @param prefix the Namespace prefix being declared.
+ * @param uri the Namespace URI the prefix is mapped to.
+ *
+ * @throws SAXException any SAX exception, possibly wrapping
+ * another exception.
+ */
+ @Override
+ public void startPrefixMapping(String prefix, String uri)
+ throws SAXException {
+ // Propagate event.
+ this.saxHandler.startPrefixMapping(prefix, uri);
+ super.startPrefixMapping(prefix, uri);
+ }
+
+ /**
+ * <i>[ContentHandler interface support]</i> Ends the scope of a
+ * prefix-URI Namespace mapping.
+ *
+ * @param prefix the prefix that was being mapped.
+ *
+ * @throws SAXException any SAX exception, possibly wrapping
+ * another exception.
+ */
+ @Override
+ public void endPrefixMapping(String prefix) throws SAXException {
+ // Propagate event.
+ this.saxHandler.endPrefixMapping(prefix);
+ super.endPrefixMapping(prefix);
+ }
+
+ /**
+ * <i>[ContentHandler interface support]</i> Receives notification
+ * of the beginning of an element.
+ *
+ * @param nsUri the Namespace URI, or the empty string if
+ * the element has no Namespace URI or if
+ * Namespace processing is not being performed.
+ * @param localName the local name (without prefix), or the
+ * empty string if Namespace processing is
+ * not being performed.
+ * @param qName the qualified name (with prefix), or the
+ * empty string if qualified names are not
+ * available.
+ * @param attrs the attributes attached to the element. If
+ * there are no attributes, it shall be an
+ * empty Attributes object.
+ *
+ * @throws SAXException any SAX exception, possibly wrapping
+ * another exception.
+ */
+ @Override
+ public void startElement(String nsUri, String localName,
+ String qName, Attributes attrs)
+ throws SAXException {
+ // Append new element to the current path.
+ this.currentPath.append('/').append(localName);
+
+ // Retrieve the matching rules for this element.
+ String eltPath = this.currentPath.substring(0);
+ Collection<XPathMatcher> matchingRules = this.getMatchingRules(eltPath, attrs);
+ if (matchingRules != null) {
+ // Matching rules found.
+ // => Make them active to trigger element building.
+ this.activeRules.put(eltPath, matchingRules);
+ }
+
+ // Propagate event.
+ if (this.activeRules.size() != 0) {
+ this.saxHandler.startElement(nsUri, localName, qName, attrs);
+ }
+ super.startElement(nsUri, localName, qName, attrs);
+ }
+
+ /**
+ * <i>[ContentHandler interface support]</i> Receives notification
+ * of the end of an element.
+ *
+ * @param nsUri the Namespace URI, or the empty string if
+ * the element has no Namespace URI or if
+ * Namespace processing is not being performed.
+ * @param localName the local name (without prefix), or the
+ * empty string if Namespace processing is
+ * not being performed.
+ * @param qName the qualified name (with prefix), or the
+ * empty string if qualified names are not
+ * available.
+ *
+ * @throws SAXException any SAX exception, possibly wrapping
+ * another exception.
+ */
+ @Override
+ public void endElement(String nsUri, String localName, String qName)
+ throws SAXException {
+ // Grab the being-built element.
+ Element elt = this.saxHandler.getCurrentElement();
+
+ // Complete element building before making use of it.
+ // (This sets the current element to the parent of elt.)
+ if (this.activeRules.size() != 0) {
+ this.saxHandler.endElement(nsUri, localName, qName);
+ }
+
+ // Get the matching rules for this element (if any).
+ String eltPath = this.currentPath.substring(0);
+ Collection<XPathMatcher> matchingRules = this.activeRules.remove(eltPath);
+ if (matchingRules != null) {
+ // Matching rules found.
+ // => Detach the current element if no rules remain active.
+ if (this.activeRules.size() == 0) {
+ elt.detach();
+ }
+
+ // And notify all matching listeners.
+ try {
+ for (XPathMatcher matcher : matchingRules) {
+ if (matcher.match(eltPath, elt)) {
+ matcher.getListener().elementMatched(eltPath, elt);
+ }
+ }
+ }
+ catch (JDOMException ex1) {
+ // Oops! Listener-originated exception.
+ // => Fire a SAXException to abort parsing.
+ throw (new SAXException(ex1.getMessage(), ex1));
+ }
+ }
+ // Remove notified element from the current path.
+ this.currentPath.setLength(
+ this.currentPath.length() - (localName.length() + 1));
+ // Propagate event.
+ super.endElement(nsUri, localName, qName);
+ }
+
+ /**
+ * <i>[ContentHandler interface support]</i> Receives notification
+ * of character data.
+ *
+ * @param ch the characters from the XML document.
+ * @param start the start position in the array.
+ * @param length the number of characters to read from the array.
+ *
+ * @throws SAXException any SAX exception, possibly wrapping
+ * another exception.
+ */
+ @Override
+ public void characters(char[] ch, int start, int length)
+ throws SAXException {
+ // Propagate event.
+ if (this.activeRules.size() != 0) {
+ this.saxHandler.characters(ch, start, length);
+ }
+ super.characters(ch, start, length);
+ }
+
+ /**
+ * <i>[ContentHandler interface support]</i> Receives notification
+ * of ignorable whitespace in element content.
+ *
+ * @param ch the characters from the XML document.
+ * @param start the start position in the array.
+ * @param length the number of characters to read from the array.
+ *
+ * @throws SAXException any SAX exception, possibly wrapping
+ * another exception.
+ */
+ @Override
+ public void ignorableWhitespace(char[] ch, int start, int length)
+ throws SAXException {
+ // Propagate event.
+ if (this.activeRules.size() != 0) {
+ this.saxHandler.ignorableWhitespace(ch, start, length);
+ }
+ super.ignorableWhitespace(ch, start, length);
+ }
+
+ /**
+ * <i>[ContentHandler interface support]</i> Receives notification
+ * of processing instruction.
+ *
+ * @param target the processing instruction target.
+ * @param data the processing instruction data, or
+ * <code>null</code> if none was supplied.
+ *
+ * @throws SAXException any SAX exception, possibly wrapping
+ * another exception.
+ */
+ @Override
+ public void processingInstruction(String target, String data)
+ throws SAXException {
+ // Propagate event.
+ if (this.activeRules.size() != 0) {
+ this.saxHandler.processingInstruction(target, data);
+ }
+ super.processingInstruction(target, data);
+ }
+
+ /**
+ * <i>[ContentHandler interface support]</i> Receives notification
+ * of a skipped entity.
+ *
+ * @param name the name of the skipped entity.
+ *
+ * @throws SAXException any SAX exception, possibly wrapping
+ * another exception.
+ */
+ @Override
+ public void skippedEntity(String name) throws SAXException {
+ // Propagate event.
+ if (this.activeRules.size() != 0) {
+ this.saxHandler.skippedEntity(name);
+ }
+ super.skippedEntity(name);
+ }
+
+
+ //=========================================================================
+ // Deviant implementations of JDOM builder objects
+ //=========================================================================
+
+ //-------------------------------------------------------------------------
+ // ParserBuilder nested class
+ //-------------------------------------------------------------------------
+
+ //-------------------------------------------------------------------------
+ // FragmentHandler nested class
+ //-------------------------------------------------------------------------
+
+ /**
+ * FragmentHandler extends SAXHandler to support matching nodes
+ * without a common ancestor. This class inserts a dummy root
+ * element in the being-built document. This prevents the document
+ * to have, from SAXHandler's point of view, multiple root
+ * elements (which would cause the parse to fail).
+ */
+ private static class FragmentHandler extends SAXHandler {
+ /**
+ * Public constructor.
+ */
+ public FragmentHandler(JDOMFactory factory) {
+ super(factory);
+
+ // Add a dummy root element to the being-built document.
+ this.pushElement(new Element("root", null, null));
+ }
+ }
}
View
21 contrib/src/java/org/jdom2/contrib/perf/PerfDoc.java
@@ -31,7 +31,6 @@
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.EntityRef;
-import org.jdom2.JDOMException;
import org.jdom2.Namespace;
import org.jdom2.ProcessingInstruction;
import org.jdom2.Text;
@@ -39,9 +38,9 @@
import org.jdom2.filter.ElementFilter;
import org.jdom2.input.DOMBuilder;
import org.jdom2.input.SAXBuilder;
-import org.jdom2.input.SAXHandler;
import org.jdom2.input.StAXEventBuilder;
import org.jdom2.input.StAXStreamBuilder;
+import org.jdom2.input.sax.SAXHandler;
import org.jdom2.output.Format;
import org.jdom2.output.SAXOutputter;
import org.jdom2.output.XMLOutputter;
@@ -51,16 +50,6 @@
@SuppressWarnings("javadoc")
public class PerfDoc {
- private static final class MySAXBuilder extends SAXBuilder {
- public MySAXBuilder() {
- super();
- }
- @Override
- public XMLReader createParser() throws JDOMException {
- return super.createParser();
- }
- }
-
private class SAXLoadRunnable implements TimeRunnable {
private final int type;
@@ -236,8 +225,7 @@ public Document subload(int type) throws Exception {
switch (type) {
case 0:
SAXBuilder sax = new SAXBuilder();
- sax.setFactory(new UncheckedJDOMFactory());
- sax.setValidation(false);
+ sax.setJDOMFactory(new UncheckedJDOMFactory());
return sax.build(car);
case 1:
DOMBuilder dom = new DOMBuilder();
@@ -255,10 +243,9 @@ public Document subload(int type) throws Exception {
XMLEventReader events = XMLInputFactory.newInstance().createXMLEventReader(car);
return staxe.build(events);
case 8:
- MySAXBuilder dsax = new MySAXBuilder();
- dsax.setValidation(false);
+ SAXBuilder dsax = new SAXBuilder();
DefaultHandler2 def = new DefaultHandler2();
- XMLReader sread = dsax.createParser();
+ XMLReader sread = dsax.getXMLReaderFactory().createXMLReader();
sread.setContentHandler(def);
sread.setDTDHandler(def);
sread.setEntityResolver(def);
View
173 core/src/java/org/jdom2/input/JAXPParserFactory.java
@@ -1,173 +0,0 @@
-/*--
-
- Copyright (C) 2000-2007 Jason Hunter & Brett McLaughlin.
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions, and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions, and the disclaimer that follows
- these conditions in the documentation and/or other materials
- provided with the distribution.
-
- 3. The name "JDOM" must not be used to endorse or promote products
- derived from this software without prior written permission. For
- written permission, please contact <request_AT_jdom_DOT_org>.
-
- 4. Products derived from this software may not be called "JDOM", nor
- may "JDOM" appear in their name, without prior written permission
- from the JDOM Project Management <request_AT_jdom_DOT_org>.
-
- In addition, we request (but do not require) that you include in the
- end-user documentation provided with the redistribution and/or in the
- software itself an acknowledgement equivalent to the following:
- "This product includes software developed by the
- JDOM Project (http://www.jdom.org/)."
- Alternatively, the acknowledgment may be graphical using the logos
- available at http://www.jdom.org/images/logos.
-
- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-
- This software consists of voluntary contributions made by many
- individuals on behalf of the JDOM Project and was originally
- created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
- Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
- on the JDOM Project, please see <http://www.jdom.org/>.
-
- */
-
-package org.jdom2.input;
-
-import java.util.*;
-
-import javax.xml.parsers.*;
-
-import org.jdom2.*;
-import org.xml.sax.*;
-
-/**
- * A non-public utility class to allocate JAXP SAX parsers.
- *
- * @author Laurent Bihanic
- */
-class JAXPParserFactory { // package protected
-
- /** JAXP 1.2 schema language property id. */
- private static final String JAXP_SCHEMA_LANGUAGE_PROPERTY =
- "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
-
- /** JAXP 1.2 schema location property id. */
- private static final String JAXP_SCHEMA_LOCATION_PROPERTY =
- "http://java.sun.com/xml/jaxp/properties/schemaSource";
-
- /**
- * Private constructor to forbid allocating instances of this utility
- * class.
- */
- private JAXPParserFactory() {
- // Never called.
- }
-
- /* Implementor's note regarding createParser() design: The features and
- properties are normally set in SAXBuilder, but we take them in
- createParser() as well because some features or properties may need to be
- applied during the JAXP parser construction. Today, for example, properties
- is used as it's the only way to configure schema validation: JAXP defines
- schema validation properties but SAX does not. This reflects in the Apache
- Xerces implementation where the SAXParser implementation supports the JAXP
- properties but the XMLReader does not. Hence, configuring schema validation
- must be done on the SAXParser object which is only visible in
- JAXParserFactory. Features is also passed in case some future JAXP release
- defines JAXP-specific features.
- */
-
- /**
- * Creates a SAX parser allocated through the configured JAXP SAX
- * parser factory.
- *
- * @param validating whether a validating parser is requested.
- * @param features the user-defined SAX features.
- * @param properties the user-defined SAX properties.
- *
- * @return a configured XMLReader.
- *
- * @throws JDOMException if any error occurred when allocating or
- * configuring the JAXP SAX parser.
- */
- public static XMLReader createParser(boolean validating,
- Map<String,Boolean> features, Map<String,Object> properties) throws JDOMException {
- try {
- SAXParser parser = null;
-
- // Allocate and configure JAXP SAX parser factory.
- SAXParserFactory factory = SAXParserFactory.newInstance();
- factory.setValidating(validating);
- factory.setNamespaceAware(true);
-
- try {
- // Allocate parser.
- parser = factory.newSAXParser();
- }
- catch (ParserConfigurationException e) {
- throw new JDOMException("Could not allocate JAXP SAX Parser", e);
- }
-
- // Set user-defined JAXP properties (if any)
- setProperty(parser, properties, JAXP_SCHEMA_LANGUAGE_PROPERTY);
- setProperty(parser, properties, JAXP_SCHEMA_LOCATION_PROPERTY);
-
- // Return configured SAX XMLReader.
- return parser.getXMLReader();
- }
- catch (SAXException e) {
- throw new JDOMException("Could not allocate JAXP SAX Parser", e);
- }
- }
-
- /**
- * Sets a property on a JAXP SAX parser object if and only if it
- * is declared in the user-defined properties.
- *
- * @param parser the JAXP SAX parser to configure.
- * @param properties the user-defined SAX properties.
- * @param name the name of the property to set.
- *
- * @throws JDOMException if any error occurred while configuring
- * the property.
- */
- private static void setProperty(SAXParser parser,
- Map<String,Object> properties, String name) throws JDOMException {
- try {
- if (properties.containsKey(name)) {
- parser.setProperty(name, properties.get(name));
- }
- }
- catch (SAXNotSupportedException e) {
- throw new JDOMException(
- name + " property not supported for JAXP parser " +
- parser.getClass().getName());
- }
- catch (SAXNotRecognizedException e) {
- throw new JDOMException(
- name + " property not recognized for JAXP parser " +
- parser.getClass().getName());
- }
- }
-}
-
View
849 core/src/java/org/jdom2/input/SAXBuilder.java
@@ -58,17 +58,10 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
-import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
-import org.jdom2.DefaultJDOMFactory;
-import org.jdom2.DocType;
-import org.jdom2.Document;
-import org.jdom2.EntityRef;
-import org.jdom2.JDOMException;
-import org.jdom2.JDOMFactory;
import org.xml.sax.DTDHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
@@ -76,10 +69,23 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
-import org.xml.sax.SAXParseException;
import org.xml.sax.XMLFilter;
import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLReaderFactory;
+
+import org.jdom2.DefaultJDOMFactory;
+import org.jdom2.DocType;
+import org.jdom2.Document;
+import org.jdom2.EntityRef;
+import org.jdom2.JDOMException;
+import org.jdom2.JDOMFactory;
+import org.jdom2.input.sax.BuilderErrorHandler;
+import org.jdom2.input.sax.XMLReaderJAXPSingletons;
+import org.jdom2.input.sax.XMLReaderJDOMFactory;
+import org.jdom2.input.sax.XMLReaderSAX2Factory;
+import org.jdom2.input.sax.SAXBuilderEngine;
+import org.jdom2.input.sax.SAXEngine;
+import org.jdom2.input.sax.SAXHandler;
+import org.jdom2.input.sax.SAXHandlerFactory;
/**
* Builds a JDOM document from files, streams, readers, URLs, or a SAX {@link
@@ -91,33 +97,76 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* document. Information about SAX can be found at <a
* href="http://www.saxproject.org">http://www.saxproject.org</a>.
* <p>
+ * For a description of how SAXBuilder is used, and how to customise the process
+ * you should look at the {@link org.jdom2.input.sax} package documentation.
+ *
* Known issues: Relative paths for a {@link DocType} or {@link EntityRef} may
* be converted by the SAX parser into absolute paths.
*
+ * @see org.jdom2.input.sax
+ *
* @author Jason Hunter
* @author Brett McLaughlin
* @author Dan Schaffer
* @author Philip Nelson
* @author Alex Rosen
+ * @author Rolf Lear
*/
-public class SAXBuilder {
-
+public class SAXBuilder implements SAXEngine {
+
+ /**
+ * For performance reasons it helps to use 'final' instances of classes.
+ * This makes the SAXHandler class a 'final' class for all normal
+ * SAXBuilders. It adds no other functionality.
+ *
+ * @author Rolf Lear
+ *
+ */
+ private static final class DefaultSAXHandler extends SAXHandler {
+ public DefaultSAXHandler(JDOMFactory factory) {
+ super(factory);
+ }
+ }
+
/**
- * Default parser class to use. This is used when no other parser
- * is given and JAXP isn't available.
+ * This SAXHandlerFactory instance provides default-configured SAXHandler
+ * instances for all non-custom situations.
+ *
+ * @author Rolf Lear
+ *
*/
- private static final String DEFAULT_SAX_DRIVER =
- "org.apache.xerces.parsers.SAXParser";
+ private static final class DefaultSAXHandlerFactory
+ implements SAXHandlerFactory {
+ @Override
+ public SAXHandler createSAXHandler(JDOMFactory factory) {
+ return new DefaultSAXHandler(factory);
+ }
+ }
+
+ private static final SAXHandlerFactory DEFAULTSAXHANDLERFAC =
+ new DefaultSAXHandlerFactory();
+
+ private static final JDOMFactory DEFAULTJDOMFAC = new DefaultJDOMFactory();
- /** Whether validation should occur */
- private boolean validate;
+ /**
+ * The XMLReader pillar of SAXBuilder
+ */
+ private XMLReaderJDOMFactory readerfac = null;
+
+ /**
+ * The SAXHandler pillar of SAXBuilder
+ */
+ private SAXHandlerFactory handlerfac = null;
+
+ /**
+ * The JDOMFactory pillar for creating new JDOM objects
+ */
+ private JDOMFactory jdomfac = null;
+
/** Whether expansion of entities should occur */
private boolean expand = true;
- /** Adapter class to use */
- private String saxDriverClass;
-
/** ErrorHandler class to use */
private ErrorHandler saxErrorHandler = null;
@@ -130,9 +179,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
/** XMLFilter instance to use */
private XMLFilter saxXMLFilter = null;
- /** The factory for creating new JDOM objects */
- private JDOMFactory factory = new DefaultJDOMFactory();
-
/** Whether to ignore ignorable whitespace */
private boolean ignoringWhite = false;
@@ -145,44 +191,35 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
/** User-specified properties to be set on the SAX parser */
private HashMap<String,Object> properties = new HashMap<String, Object>(5);
- /** Whether to use fast parser reconfiguration */
- private boolean fastReconfigure = false;
-
- /** Whether to try lexical reporting in fast parser reconfiguration */
- private boolean skipNextLexicalReportingConfig = false;
-
- /** Whether to to try entity expansion in fast parser reconfiguration */
- private boolean skipNextEntityExpandConfig = false;
-
/**
* Whether parser reuse is allowed.
* <p>Default: <code>true</code></p>
*/
private boolean reuseParser = true;
-
+
/** The current SAX parser, if parser reuse has been activated. */
- private XMLReader saxParser = null;
+ private SAXEngine engine = null;
/**
- * Creates a new SAXBuilder which will attempt to first locate
- * a parser via JAXP, then will try to use a set of default
- * SAX Drivers. The underlying parser will not validate.
+ * Creates a new JAXP-based SAXBuilder. The underlying parser will not
+ * validate.
*/
public SAXBuilder() {
- this(false);
+ this(XMLReaderJAXPSingletons.NONVALIDATING);
}
/**
- * Creates a new SAXBuilder which will attempt to first locate
- * a parser via JAXP, then will try to use a set of default
- * SAX Drivers. The underlying parser will validate or not
- * according to the given parameter.
+ * Creates a new JAXP-based SAXBuilder. The underlying parser will validate
+ * (using DTD) or not according to the given parameter. If you want Schema
+ * validation then use SAXBuilder(JAXPXMLReaderSingleton.XSDVALIDATOR)
*
- * @param validate <code>boolean</code> indicating if
+ * @param validate <code>boolean</code> indicating if DTD
* validation should occur.
*/
public SAXBuilder(boolean validate) {
- this.validate = validate;
+ this(validate
+ ? XMLReaderJAXPSingletons.DTDVALIDATING
+ : XMLReaderJAXPSingletons.NONVALIDATING);
}
/**
@@ -207,8 +244,46 @@ public SAXBuilder(String saxDriverClass) {
* validation should occur.
*/
public SAXBuilder(String saxDriverClass, boolean validate) {
- this.saxDriverClass = saxDriverClass;
- this.validate = validate;
+ this(new XMLReaderSAX2Factory(validate, saxDriverClass));
+ }
+
+ /**
+ * Creates a new SAXBuilder. This is the base constructor for all other
+ * SAXBuilder constructors: they all find a way to create a
+ * JDOMXMLReaderFactory and then call this constructor with that factory.
+ * <p>
+ * @see XMLReaderJDOMFactory and its package documentation for details on
+ * creating and using JDOMXMLReaderFactories.
+ * @param readersouce the {@link XMLReaderJDOMFactory} that supplies
+ * XMLReaders. If the value is null then a Non-Validating JAXP-based
+ * SAX2.0 parser will be used.
+ */
+ public SAXBuilder(XMLReaderJDOMFactory readersouce) {
+ this(readersouce, null, null);
+ }
+
+ /**
+ * Creates a new SAXBuilder. This is the base constructor for all other
+ * SAXBuilder constructors: they all find a way to create a
+ * JDOMXMLReaderFactory and then call this constructor with that factory.
+ * <p>
+ * @see XMLReaderJDOMFactory and its package documentation for details on
+ * creating and using JDOMXMLReaderFactories.
+ *
+ * @param xmlreaderfactory a {@link XMLReaderJDOMFactory} that creates XMLReaders
+ * @param handlerfactory a {@link SAXHandlerFactory} that creates SAXHandlers
+ * @param jdomfactory a {@link JDOMFactory} that creates JDOM Content.
+ */
+ public SAXBuilder(XMLReaderJDOMFactory xmlreaderfactory, SAXHandlerFactory handlerfactory, JDOMFactory jdomfactory) {
+ this.readerfac = xmlreaderfactory == null
+ ? XMLReaderJAXPSingletons.NONVALIDATING
+ : xmlreaderfactory;
+ this.handlerfac = handlerfactory == null
+ ? DEFAULTSAXHANDLERFAC
+ : handlerfactory;
+ this.jdomfac = jdomfactory == null
+ ? DEFAULTJDOMFAC
+ : jdomfactory;
}
/**
@@ -217,15 +292,29 @@ public SAXBuilder(String saxDriverClass, boolean validate) {
* @return the driver class assigned in the constructor
*/
public String getDriverClass() {
- return saxDriverClass;
+ if (readerfac instanceof XMLReaderSAX2Factory) {
+ return ((XMLReaderSAX2Factory)readerfac).getDriverClassName();
+ }
+ return null;
}
/**
* Returns the current {@link org.jdom2.JDOMFactory} in use.
* @return the factory in use
+ * @deprecated is replaced by {@link #getJDOMFactory()}
*/
+ @Deprecated
public JDOMFactory getFactory() {
- return factory;
+ return getJDOMFactory();
+ }
+
+ /**
+ * Returns the current {@link org.jdom2.JDOMFactory} in use.
+ * @return the factory in use
+ */
+ @Override
+ public JDOMFactory getJDOMFactory() {
+ return jdomfac;
}
/**
@@ -233,34 +322,119 @@ public JDOMFactory getFactory() {
* the tree with your own subclasses of the JDOM classes.
*
* @param factory <code>JDOMFactory</code> to use
+ * @deprecated use {@link #setJDOMFactory(JDOMFactory)}
*/
+ @Deprecated
public void setFactory(JDOMFactory factory) {
- this.factory = factory;
+ setJDOMFactory(factory);
+ }
+
+ /**
+ * This sets a custom JDOMFactory for the builder. Use this to build
+ * the tree with your own subclasses of the JDOM classes.
+ *
+ * @param factory <code>JDOMFactory</code> to use
+ */
+ public void setJDOMFactory(JDOMFactory factory) {
+ this.jdomfac = factory;
+ engine = null;
+ }
+
+ /**
+ * Get the current XMLReader factory.
+ * @return the current JDOMXMLReaderFactory
+ */
+ public XMLReaderJDOMFactory getXMLReaderFactory() {
+ return readerfac;
+ }
+
+ /**
+ * Set the current XMLReader factory.
+ * @param rfac the JDOMXMLReaderFactory to set.
+ */
+ public void setXMLReaderFactory(XMLReaderJDOMFactory rfac) {
+ readerfac = rfac == null
+ ? XMLReaderJAXPSingletons.NONVALIDATING
+ : rfac;
+ engine = null;
+ }
+
+
+ /**
+ * Get the SAXHandlerFactory used to supply SAXHandlers to this SAXBuilder.
+ * @return the current SAXHandlerFactory (never null).
+ */
+ public SAXHandlerFactory getSAXHandlerFactory() {
+ return handlerfac;
+ }
+
+ /**
+ * Set the SAXHandlerFactory to be used by this SAXBuilder.
+ * @param factory the required SAXHandlerFactory. Null input will request
+ * the default SAXHandlerFactory.
+ */
+ public void setSAXHandlerFactory(SAXHandlerFactory factory) {
+ this.handlerfac = factory == null ? DEFAULTSAXHANDLERFAC : factory;
+ engine = null;
}
/**
* Returns whether validation is to be performed during the build.
*
* @return whether validation is to be performed during the build
+ * @deprecated in lieu of {@link #isValidating()}
*/
+ @Deprecated
public boolean getValidation() {
- return validate;
+ return isValidating();
+ }
+
+ /**
+ * Returns whether validation is to be performed during the build.
+ *
+ * @return whether validation is to be performed during the build
+ */
+ @Override
+ public boolean isValidating() {
+ return readerfac.isValidating();
}
/**
* This sets validation for the builder.
+ * <p>
+ * <b>Do Not Use</b>
+ * <p>
+ * JDOM2 introduces the concept of XMLReader factories. The XMLReader is
+ * what determines the type of validation. A simple boolean is not enough
+ * to indicate what sort of validation is required. The
+ * {@link #setXMLReaderFactory(XMLReaderJDOMFactory)} method provides a
+ * means to me more specific about validation.
+ * <p>
+ * For backward compatibility this method has been retained, but its use is
+ * discouraged. It does make some logical choices though. The code is
+ * equivalent to:
+ * <p>
+ * <pre>setXMLReaderFactory(JAXPXMLReaderSingleton.DTDVALIDATING)</pre> for
+ * true, and
+ * <pre>setXMLReaderFactory(JAXPXMLReaderSingleton.NONVALIDATING)</pre> for
+ * false.
*
* @param validate <code>boolean</code> indicating whether validation
* should occur.
+ * @deprecated use {@link #setXMLReaderFactory(XMLReaderJDOMFactory)}
*/
+ @Deprecated
public void setValidation(boolean validate) {
- this.validate = validate;
+ setXMLReaderFactory(validate
+ ? XMLReaderJAXPSingletons.DTDVALIDATING
+ : XMLReaderJAXPSingletons.NONVALIDATING);
}
/**
* Returns the {@link ErrorHandler} assigned, or null if none.
* @return the ErrorHandler assigned, or null if none
*/
+ @Override
public ErrorHandler getErrorHandler() {
return saxErrorHandler;
}
@@ -272,6 +446,7 @@ public ErrorHandler getErrorHandler() {
*/
public void setErrorHandler(ErrorHandler errorHandler) {
saxErrorHandler = errorHandler;
+ engine = null;
}
/**
@@ -279,6 +454,7 @@ public void setErrorHandler(ErrorHandler errorHandler) {
*
* @return the EntityResolver assigned
*/
+ @Override
public EntityResolver getEntityResolver() {
return saxEntityResolver;
}
@@ -290,6 +466,7 @@ public EntityResolver getEntityResolver() {
*/
public void setEntityResolver(EntityResolver entityResolver) {
saxEntityResolver = entityResolver;
+ engine = null;
}
/**
@@ -297,6 +474,7 @@ public void setEntityResolver(EntityResolver entityResolver) {
*
* @return the DTDHandler assigned
*/
+ @Override
public DTDHandler getDTDHandler() {
return saxDTDHandler;
}
@@ -308,6 +486,7 @@ public DTDHandler getDTDHandler() {
*/
public void setDTDHandler(DTDHandler dtdHandler) {
saxDTDHandler = dtdHandler;
+ engine = null;
}
/**
@@ -326,6 +505,7 @@ public XMLFilter getXMLFilter() {
*/
public void setXMLFilter(XMLFilter xmlFilter) {
saxXMLFilter = xmlFilter;
+ engine = null;
}
/**
@@ -334,8 +514,22 @@ public void setXMLFilter(XMLFilter xmlFilter) {
*
* @return whether element content whitespace is to be ignored during the
* build
+ * @deprecated in lieu of {@link #isIgnoringElementContentWhitespace()}
*/
+ @Deprecated
public boolean getIgnoringElementContentWhitespace() {
+ return isIgnoringElementContentWhitespace();
+ }
+
+ /**
+ * Returns whether element content whitespace is to be ignored during the
+ * build.
+ *
+ * @return whether element content whitespace is to be ignored during the
+ * build
+ */
+ @Override
+ public boolean isIgnoringElementContentWhitespace() {
return ignoringWhite;
}
@@ -352,6 +546,7 @@ public boolean getIgnoringElementContentWhitespace() {
*/
public void setIgnoringElementContentWhitespace(boolean ignoringWhite) {
this.ignoringWhite = ignoringWhite;
+ engine = null;
}
/**
@@ -362,10 +557,27 @@ public void setIgnoringElementContentWhitespace(boolean ignoringWhite) {
* be ignored during build.
*
* @see #setIgnoringBoundaryWhitespace
+ * @deprecated in lieu of {@link #isIgnoringBoundaryWhitespace()}
*/
+ @Deprecated
public boolean getIgnoringBoundaryWhitespace() {
+ return isIgnoringBoundaryWhitespace();
+ }
+
+ /**
+ * Returns whether or not the parser will elminate element content
+ * containing only whitespace.
+ *
+ * @return <code>boolean</code> - whether only whitespace content will
+ * be ignored during build.
+ *
+ * @see #setIgnoringBoundaryWhitespace
+ */
+ @Override
+ public boolean isIgnoringBoundaryWhitespace() {
return ignoringBoundaryWhite;
}
+
/**
* Specifies whether or not the parser should elminate boundary whitespace,
@@ -381,10 +593,62 @@ public boolean getIgnoringBoundaryWhitespace() {
* whitespace. The default is <code>false</code>.
*
* @param ignoringBoundaryWhite Whether to ignore whitespace-only text
- * noes
+ * nodes
*/
public void setIgnoringBoundaryWhitespace(boolean ignoringBoundaryWhite) {
this.ignoringBoundaryWhite = ignoringBoundaryWhite;
+ engine = null;
+ }
+
+ /**
+ * Returns whether or not entities are being expanded into normal text
+ * content.
+ *
+ * @return whether entities are being expanded
+ * @deprecated in lieu of {@link #isExpandEntities()}
+ */
+ @Deprecated
+ public boolean getExpandEntities() {
+ return expand;
+ }
+
+ /**
+ * Returns whether or not entities are being expanded into normal text
+ * content.
+ *
+ * @return whether entities are being expanded
+ */
+ @Override
+ public boolean isExpandEntities() {
+ return expand;
+ }
+
+ /**
+ * <p>
+ * This sets whether or not to expand entities for the builder.
+ * A true means to expand entities as normal content. A false means to
+ * leave entities unexpanded as <code>EntityRef</code> objects. The
+ * default is true.
+ * </p>
+ * <p>
+ * When this setting is false, the internal DTD subset is retained; when
+ * this setting is true, the internal DTD subset is not retained.
+ * </p>
+ * <p>
+ * Note that Xerces (at least up to 1.4.4) has a bug where entities
+ * in attribute values will be misreported if this flag is turned off,
+ * resulting in entities to appear within element content. When turning
+ * entity expansion off either avoid entities in attribute values, or
+ * use another parser like Crimson.
+ * http://nagoya.apache.org/bugzilla/show_bug.cgi?id=6111
+ * </p>
+ *
+ * @param expand <code>boolean</code> indicating whether entity expansion
+ * should occur.
+ */
+ public void setExpandEntities(boolean expand) {
+ this.expand = expand;
+ engine = null;
}
/**
@@ -412,7 +676,9 @@ public boolean getReuseParser() {
*/
public void setReuseParser(boolean reuseParser) {
this.reuseParser = reuseParser;
- this.saxParser = null;
+ if (!reuseParser) {
+ engine = null;
+ }
}
/**
@@ -427,11 +693,11 @@ public void setReuseParser(boolean reuseParser) {
* calling this has no effect.
*
* @param fastReconfigure Whether to do a fast reconfiguration of the parser
+ * @deprecated All reused Parsers are now fast-reconfigured. No need to set it.
*/
+ @Deprecated
public void setFastReconfigure(boolean fastReconfigure) {
- if (this.reuseParser) {
- this.fastReconfigure = fastReconfigure;
- }
+ // do nothing
}
/**
@@ -453,6 +719,7 @@ public void setFastReconfigure(boolean fastReconfigure) {
public void setFeature(String name, boolean value) {
// Save the specified feature for later.
features.put(name, value ? Boolean.TRUE : Boolean.FALSE);
+ engine = null;
}
/**
@@ -473,191 +740,78 @@ public void setFeature(String name, boolean value) {
public void setProperty(String name, Object value) {
// Save the specified property for later.
properties.put(name, value);
+ engine = null;
}
/**
- * This builds a document from the supplied
- * input source.
- *
- * @param in <code>InputSource</code> to read from
- * @return <code>Document</code> resultant Document object
- * @throws JDOMException when errors occur in parsing
- * @throws IOException when an I/O error prevents a document
- * from being fully parsed
+ * This method builds a SAXBuilderEngine that can be reused many times, but
+ * not concurrently.
+ * @return a {@link SAXBuilderEngine} representing the current state of the
+ * current SAXBuilder settings.
+ * @throws JDOMException if there is any problem initialising the engine.
*/
- public Document build(InputSource in)
- throws JDOMException, IOException {
- SAXHandler contentHandler = null;
+ public SAXEngine buildEngine() throws JDOMException {
- try {
- // Create and configure the content handler.
- contentHandler = createContentHandler();
- configureContentHandler(contentHandler);
-
- XMLReader parser = this.saxParser;
- if (parser == null) {
- // Create and configure the parser.
- parser = createParser();
-
- // Install optional filter
- if (saxXMLFilter != null) {
- // Connect filter chain to parser
- XMLFilter root = saxXMLFilter;
- while (root.getParent() instanceof XMLFilter) {
- root = (XMLFilter)root.getParent();
- }
- root.setParent(parser);
-
- // Read from filter
- parser = saxXMLFilter;
- }
-
- // Configure parser
- configureParser(parser, contentHandler);
-
- if (reuseParser) {
- this.saxParser = parser;
- }
- }
- else {
- // Reset content handler as SAXHandler instances cannot
- // be reused
- configureParser(parser, contentHandler);
- }
-
- // Parse the document.
- parser.parse(in);
-
- return contentHandler.getDocument();
- }
- catch (SAXParseException e) {
- Document doc = contentHandler.getDocument();
- if (doc.hasRootElement() == false) {
- doc = null;
- }
-
- String systemId = e.getSystemId();
- if (systemId != null) {
- throw new JDOMParseException("Error on line " +
- e.getLineNumber() + " of document " + systemId, e, doc);
- }
- throw new JDOMParseException("Error on line " +
- e.getLineNumber(), e, doc);
- }
- catch (SAXException e) {
- throw new JDOMParseException("Error in building: " +
- e.getMessage(), e, contentHandler.getDocument());
- }
- finally {
- // Explicitly nullify the handler to encourage GC
- // It's a stack var so this shouldn't be necessary, but it
- // seems to help on some JVMs
- contentHandler = null;
- }
- }
-
- /**
- * This creates the SAXHandler that will be used to build the Document.
- *
- * @return <code>SAXHandler</code> - resultant SAXHandler object.
- */
- protected SAXHandler createContentHandler() {