diff --git a/core/src/java/org/jdom2/input/DefaultStAXFilter.java b/core/src/java/org/jdom2/input/DefaultStAXFilter.java new file mode 100644 index 000000000..e2aedbfe8 --- /dev/null +++ b/core/src/java/org/jdom2/input/DefaultStAXFilter.java @@ -0,0 +1,81 @@ +package org.jdom2.input; + +import org.jdom2.Namespace; + +/** + * This DefaultStAXFilter includes all content and prunes nothing. + *
+ * Override this class to make adjustments to get the results you need. + * + * @see StAXFilter + * + * @author Rolf Lear + */ +public class DefaultStAXFilter implements StAXFilter { + + @Override + public boolean includeDocType() { + return true; + } + + @Override + public boolean includeElement(final int depth, final String name, final Namespace ns) { + return true; + } + + @Override + public String includeComment(final int depth, final String comment) { + return comment; + } + + @Override + public boolean includeEntityRef(final int depth, final String name) { + return true; + } + + @Override + public String includeCDATA(final int depth, final String text) { + return text; + } + + @Override + public String includeText(final int depth, final String text) { + return text; + } + + @Override + public boolean includeProcessingInstruction(final int depth, final String target) { + return true; + } + + @Override + public boolean pruneElement(final int depth, final String name, final Namespace ns) { + return false; + } + + @Override + public String pruneComment(final int depth, final String comment) { + return comment; + } + + @Override + public boolean pruneEntityRef(final int depth, final String name) { + return false; + } + + @Override + public String pruneCDATA(final int depth, final String text) { + return text; + } + + @Override + public String pruneText(final int depth, final String text) { + return text; + } + + @Override + public boolean pruneProcessingInstruction(final int depth, final String target) { + return false; + } + +} diff --git a/core/src/java/org/jdom2/input/StAXFilter.java b/core/src/java/org/jdom2/input/StAXFilter.java new file mode 100644 index 000000000..4b7739276 --- /dev/null +++ b/core/src/java/org/jdom2/input/StAXFilter.java @@ -0,0 +1,243 @@ +package org.jdom2.input; + +import org.jdom2.Namespace; + +/** + * In StAX Processing it is possible to read fragments of XML. JDOM supports + * reading JDOM Content from StAX Readers in fragments. JDOM users can influence + * the content that is processed by the return values in this interface. + *
+ * Using the StAXStreamBuilder or StAXEventBuilder you can parse a List of + * JDOM content by filtering that content with an instance of this filter. + *
+ * There are two significant states in which methods in this interface will be + * called: + *
+ * If the return value of this call is true, then this Element will be + * processed as a JDOM fragment. You may then get calls to the prune* + * methods to determine whether child content of this Element should be + * pruned + * @param depth The depth of this content from the document root + * (the root Element is at depth 0) + * @param name The XML tag name of this Element + * @param ns The Namespace of this Element + * @return true if the Element should become a JDOM Fragment. + */ + public boolean includeElement(int depth, String name, Namespace ns); + + /** + * The current event is a Comment event. + *
+ * A non-null return value will become the Comment text. Return null to skip + * the comment, the specified comment text to leave the comment unchanged, + * or any other String to create a different comment value. + *
+ * To include the comment as-is, do:
+ *
+ *
+ * public String includeComment(int depth, String comment) { + * return comment; + * } + *+ * @param depth The depth of this content from the document root + * (the root Element is at depth 0) + * @param comment The Comment value + * @return null if you want to exclude this comment, or a non-null value + * which will become the new comment value. + */ + public String includeComment(int depth, String comment); + + /** + * The current event is an EntityRef event. + *
+ * @param depth The depth of this content from the document root + * (the root Element is at depth 0) + * @param name The EntityRef name + * @return true if you want to include this EntityRef. + */ + public boolean includeEntityRef(int depth, String name); + + /** + * The current event is a CDATA event. + *
+ * A non-null return value will become the CDATA text. Return null to skip + * the CDATA, the specified text to leave the CDATA unchanged, + * or any other String to create a different CDATA value. + *
+ * To include the CDATA as-is, do:
+ *
+ *
+ * public String includeCDATA(int depth, String text) { + * return text; + * } + *+ * @param depth The depth of this content from the document root + * (the root Element is at depth 0) + * @param text The CDATA text value + * @return null if you want to exclude this CDATA, or a non-null value + * which will become the new CDATA text value. + */ + public String includeCDATA(int depth, String text); + + /** + * The current event is a TEXT event. + *
+ * A non-null return value will become the Text text. Return null to skip + * the Text, the specified text to leave the Text unchanged, + * or any other String to create a different Text value. + *
+ * To include the Text as-is, do:
+ *
+ *
+ * public String includeText(int depth, String text) { + * return text; + * } + *+ * @param depth The depth of this content from the document root + * (the root Element is at depth 0) + * @param text The Text value + * @return null if you want to exclude this Text, or a non-null value + * which will become the new Text value. + */ + public String includeText(int depth, String text); + + /** + * The current event is a ProcessingInstruction event. + *
+ * @param depth The depth of this content from the document root + * (the root Element is at depth 0) + * @param target The ProcessingInstruction Target value + * @return true if you want to include this ProcessingInstruction. + */ + public boolean includeProcessingInstruction(int depth, String target); + + /** + * An Element is being included, and this is a child Element event of the + * included parent Element. Should this Child Element be pruned from the + * parent fragment? + * @param depth The depth of this content from the document root + * (the root Element is at depth 0) + * @param name The XML tag name of this child Element + * @param ns The Namespace of this child Element + * @return true if the child Element should be excluded. + */ + public boolean pruneElement(int depth, String name, Namespace ns); + + + /** + * An Element is being included, and this is a child Comment event of the + * included parent Element. Should this child Comment be pruned from the + * parent fragment? + *
+ * A non-null return value will become the Comment value. Return null to + * skip the Coment, the specified comment to leave the Comment unchanged, + * or any other String to create a different Comment value. + *
+ * To include the Comment as-is, do:
+ *
+ *
+ * public String pruneComment(int depth, String comment) { + * return comment; + * } + *+ * @param depth The depth of this content from the document root + * (the root Element is at depth 0) + * @param comment The Comment value + * @return null if you want to exclude this Comment, or a non-null value + * which will become the new Comment value. + */ + public String pruneComment(int depth, String comment); + + /** + * An Element is being included, and this is a child EntityRef event of the + * included parent Element. Should this child EntityRef be pruned from the + * parent fragment? + *
+ * @param depth The depth of this content from the document root + * (the root Element is at depth 0) + * @param name The EntityRef name + * @return true if you want to exclude this EntityRef. + */ + public boolean pruneEntityRef(int depth, String name); + + /** + * An Element is being included, and this is a child CDATA event of the + * included parent Element. Should this child CDATA be pruned from the + * parent fragment? + *
+ * A non-null return value will become the CDATA text. Return null to skip + * the CDATA, the specified text to leave the CDATA unchanged, + * or any other String to create a different CDATA value. + *
+ * To include the CDATA as-is, do:
+ *
+ *
+ * public String pruneCDATA(int depth, String text) { + * return text; + * } + *+ * @param depth The depth of this content from the document root + * (the root Element is at depth 0) + * @param text The CDATA text value + * @return null if you want to exclude this CDATA, or a non-null value + * which will become the new CDATA text value. + */ + public String pruneCDATA(int depth, String text); + + /** + * An Element is being included, and this is a child Text event of the + * included parent Element. Should this child Text be pruned from the + * parent fragment? + *
+ * A non-null return value will become the Text. Return null to skip + * the Text, the specified text to leave the Text unchanged, + * or any other String to create a different Text value. + *
+ * To include the Text as-is, do:
+ *
+ *
+ * public String pruneText(int depth, String text) { + * return text; + * } + *+ * @param depth The depth of this content from the document root + * (the root Element is at depth 0) + * @param text The Text value + * @return null if you want to exclude this Text, or a non-null value + * which will become the new Text value. + */ + public String pruneText(int depth, String text); + + /** + * An Element is being included, and this is a child ProcessingInstruction + * event of the included parent Element. Should this ProcessingInstruction + * be pruned from the parent fragment? + *
+ * @param depth The depth of this content from the document root
+ * (the root Element is at depth 0)
+ * @param target The ProcessingInstruction Target value
+ * @return true if you want to exclude this ProcessingInstruction.
+ */
+ public boolean pruneProcessingInstruction(int depth, String target);
+
+}
diff --git a/core/src/java/org/jdom2/input/StAXStreamBuilder.java b/core/src/java/org/jdom2/input/StAXStreamBuilder.java
index 72af8ba5a..660625c3d 100644
--- a/core/src/java/org/jdom2/input/StAXStreamBuilder.java
+++ b/core/src/java/org/jdom2/input/StAXStreamBuilder.java
@@ -54,11 +54,16 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
package org.jdom2.input;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.jdom2.AttributeType;
+import org.jdom2.Content;
import org.jdom2.DefaultJDOMFactory;
import org.jdom2.DocType;
import org.jdom2.Document;
@@ -101,7 +106,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
*
*/
public class StAXStreamBuilder implements XMLStreamConstants {
-
+
/**
* Create a Document from an XMLStreamReader
* @param factory The {@link JDOMFactory} to use
@@ -114,10 +119,6 @@ private static final Document process(final JDOMFactory factory,
final XMLStreamReader stream) throws JDOMException {
try {
- final Document document = factory.document(null);
-
- Element current = null;
-
int state = stream.getEventType();
if (XMLStreamConstants.START_DOCUMENT != state) {
@@ -125,6 +126,8 @@ private static final Document process(final JDOMFactory factory,
"are at their beginning when being processed.");
}
+ final Document document = factory.document(null);
+
while (state != XMLStreamConstants.END_DOCUMENT) {
switch (state) {
@@ -139,7 +142,7 @@ private static final Document process(final JDOMFactory factory,
document.setProperty("ENCODING",
stream.getEncoding());
break;
-
+
case DTD:
final DocType dtype = DTDParser.parse(
stream.getText(), factory);
@@ -147,79 +150,347 @@ private static final Document process(final JDOMFactory factory,
break;
case START_ELEMENT:
- final Element emt = processElement(factory, stream);
- if (current == null) {
- document.setRootElement(emt);
- final DocType dt = document.getDocType();
- if (dt != null) {
- dt.setElementName(emt.getName());
- }
+ document.setRootElement(processElementFragment(factory, stream));
+ break;
+
+ case END_ELEMENT:
+ throw new JDOMException("Unexpected XMLStream event at Document level: END_ELEMENT");
+ case ENTITY_REFERENCE:
+ throw new JDOMException("Unexpected XMLStream event at Document level: ENTITY_REFERENCE");
+ case CDATA:
+ throw new JDOMException("Unexpected XMLStream event at Document level: CDATA");
+ case SPACE:
+ throw new JDOMException("Unexpected XMLStream event at Document level: SPACE");
+ case CHARACTERS:
+ throw new JDOMException("Unexpected XMLStream event at Document level: CHARACTERS");
+
+ case COMMENT:
+ document.addContent(
+ factory.comment(stream.getText()));
+ break;
+
+ case PROCESSING_INSTRUCTION:
+ document.addContent(factory.processingInstruction(
+ stream.getPITarget(), stream.getPIData()));
+ break;
+
+ default:
+ throw new JDOMException("Unexpected XMLStream event " + state);
+
+ }
+ if (stream.hasNext()) {
+ state = stream.next();
+ } else {
+ throw new JDOMException("Unexpected end-of-XMLStreamReader");
+ }
+ }
+ return document;
+ } catch (final XMLStreamException xse) {
+ throw new JDOMException("Unable to process XMLStream. See Cause.", xse);
+ }
+ }
+
+ private ListJDOMFactory
to use
*/
public void setFactory(JDOMFactory factory) {
- this.factory = factory;
+ this.builderfactory = factory;
}
-
+
/**
* This builds a document from the supplied
* XMLStreamReader.
@@ -279,7 +550,33 @@ public void setFactory(JDOMFactory factory) {
* @throws JDOMException when errors occur in parsing
*/
public Document build(XMLStreamReader reader) throws JDOMException {
- return process(factory, reader);
+ return process(builderfactory, reader);
+ }
+
+ /**
+ * Read the entire XMLStreamReader and from it build a list of Content that
+ * conforms to the rules in the supplied StAXFilter.
+ * @param reader The XMLStreamReader to parse
+ * @param filter The Filter to use for the Content
+ * @return a List of Content that were identified by the supplied filter
+ * @throws JDOMException if there was a parsing problem.
+ */
+ public List