--- a/jaxp/src/java.xml/share/classes/com/sun/xml/internal/stream/EventFilterSupport.java Thu Jan 26 19:22:38 2017 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/xml/internal/stream/EventFilterSupport.java Thu Jan 26 21:20:30 2017 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -64,7 +64,7 @@
}
public XMLEvent nextEvent()throws XMLStreamException{
- if(super.hasNext()){
+ while (super.hasNext()) {
//get the next event by calling XMLEventReader
XMLEvent event = super.nextEvent();
@@ -72,27 +72,19 @@
if(fEventFilter.accept(event)){
return event;
}
- else{
- return nextEvent();
- }
- }else{
- throw new NoSuchElementException();
}
+ throw new NoSuchElementException();
}//nextEvent()
public XMLEvent nextTag() throws XMLStreamException{
- if(super.hasNext()){
+ while (super.hasNext()) {
XMLEvent event = super.nextTag();
//if the filter accepts this event return this event.
if(fEventFilter.accept(event)){
return event;
}
- else{
- return nextTag();
- }
- }else{
- throw new NoSuchElementException();
}
+ throw new NoSuchElementException();
}
public XMLEvent peek() throws XMLStreamException{
--- a/jaxp/test/ProblemList.txt Thu Jan 26 19:22:38 2017 +0000
+++ b/jaxp/test/ProblemList.txt Thu Jan 26 21:20:30 2017 +0000
@@ -1,6 +1,6 @@
###########################################################################
#
-# Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -22,5 +22,3 @@
# questions.
#
###########################################################################
-
-javax/xml/jaxp/isolatedjdk/catalog/PropertiesTest.sh 8169827 generic-all
--- a/jaxp/test/javax/xml/jaxp/isolatedjdk/IsolatedJDK.sh Thu Jan 26 19:22:38 2017 +0000
+++ b/jaxp/test/javax/xml/jaxp/isolatedjdk/IsolatedJDK.sh Thu Jan 26 21:20:30 2017 +0000
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -21,11 +21,6 @@
# or visit www.oracle.com if you need additional information or have any
# questions.
-if [ $# = 0 ]; then
- echo "The suffix of ISOLATED_JDK is mandatory"
- exit 1
-fi
-
checkVariable() {
variable='$'$1
@@ -42,20 +37,33 @@
done
}
-# Check essential variables
-checkVariables TESTJAVA TESTSRC TESTCLASSES TESTCLASSPATH
+# Script needs parameters
+if [ $# = 0 ]; then
+ echo "Syntax: IsolatedJDK.sh <Suffix> [remove]"
+ exit 1
+fi
-echo "TESTJAVA=${TESTJAVA}"
-echo "TESTSRC=${TESTSRC}"
-echo "TESTCLASSES=${TESTCLASSES}"
-echo "TESTCLASSPATH=${TESTCLASSPATH}"
+# Is it the call to remove ?
+if [ $# = 2 ]; then
+ if [ "$2" = "remove" ]; then
+ removeIsolatedJdk=1
+ fi
+fi
+
+# Check essential variables
+checkVariables TESTJAVA
+ISOLATED_JDK="./ISOLATED_JDK_$1"
+
+# Remove isolated copy
+if [ "$removeIsolatedJdk" = "1" ]; then
+ echo "Removing ${ISOLATED_JDK}..."
+ rm -rf ${ISOLATED_JDK}
+ echo "Removed."
+ exit 0
+fi
# Make an isolated copy of the testing JDK
-ISOLATED_JDK="./ISOLATED_JDK_$1"
-echo "ISOLATED_JDK=${ISOLATED_JDK}"
-
-echo "Copy testing JDK started"
+echo "Copying test JDK: ${TESTJAVA} -> ${ISOLATED_JDK}..."
cp -H -R ${TESTJAVA} ${ISOLATED_JDK} || exit 1
chmod -R +w ${ISOLATED_JDK} || exit 1
-echo "Copy testing JDK ended"
-
+echo "Copy done."
--- a/jaxp/test/javax/xml/jaxp/isolatedjdk/catalog/PropertiesTest.java Thu Jan 26 19:22:38 2017 +0000
+++ b/jaxp/test/javax/xml/jaxp/isolatedjdk/catalog/PropertiesTest.java Thu Jan 26 21:20:30 2017 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -37,20 +37,95 @@
import static catalog.CatalogTestUtils.generateJAXPProps;
import static catalog.CatalogTestUtils.getCatalogPath;
+import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import javax.xml.catalog.CatalogResolver;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
/*
- * This case tests if the properties FILES, DEFER, PREFER, RESOLVE in
- * jaxp.properties and system properties could be cared.
+ * @test
+ * @library /javax/xml/jaxp/libs /javax/xml/jaxp/isolatedjdk
+ * @run shell/timeout=600 ../IsolatedJDK.sh JAXP_PROPS
+ * @run testng catalog.PropertiesTest
+ * @run shell/timeout=600 ../IsolatedJDK.sh JAXP_PROPS remove
+ * @summary This test case tests if the properties FILES, DEFER, PREFER,
+ * RESOLVE in jaxp.properties and system properties are used.
+ * It needs to run in a copied JDK as it modifies the JDK's
+ * jaxp.properties file.
+ * @bug 8077931
*/
public class PropertiesTest {
private static final String CATALOG_PROPERTIES = "properties.xml";
+ @Test
+ /*
+ * Run main in a child process as it will modify the JDK.
+ */
+ public void test() throws Exception {
+ // get required properties and do some assertions
+ String javaclasspath = System.getProperty("java.class.path");
+ Assert.assertNotNull(javaclasspath, "Test class path is null");
+ String testclasspath = System.getProperty("test.class.path");
+ Assert.assertNotNull(testclasspath, "Test class path is null");
+ String testsourcepath = System.getProperty("test.src");
+ Assert.assertNotNull(testsourcepath, "Test source path is null");
+
+ // start the child process
+ List<String> testCall = new ArrayList<>(6);
+ testCall.add(Paths.get("ISOLATED_JDK_JAXP_PROPS", "/bin", "java").toString());
+ testCall.add("-cp");
+ testCall.add(javaclasspath);
+ testCall.add("-Dtest.class.path=" + testclasspath);
+ testCall.add("-Dtest.src=" + testsourcepath);
+ testCall.add("catalog.PropertiesTest");
+ System.out.println("Starting child process: " + Arrays.toString(testCall.toArray()));
+ Process test = new ProcessBuilder(testCall).start();
+
+ // wait for it to finish
+ boolean interrupted = false;
+ do {
+ try {
+ test.waitFor();
+ interrupted = false;
+ } catch (InterruptedException ie) {
+ interrupted = true;
+ }
+ } while (interrupted);
+
+ // trace system.out of child process
+ System.out.println("Proccess Out:");
+ BufferedReader br = new BufferedReader(new InputStreamReader(test.getInputStream()));
+ String line;
+ while ((line = br.readLine()) != null) {
+ System.out.println(line);
+ }
+ br.close();
+
+ // trace system.err of child process
+ System.out.println("Proccess Err:");
+ br = new BufferedReader(new InputStreamReader(test.getErrorStream()));
+ while ((line = br.readLine()) != null) {
+ System.out.println(line);
+ }
+ br.close();
+
+ // trace exit value and assert 0
+ int exitValue = test.exitValue();
+ System.out.println("Process Exit code: " + exitValue);
+ Assert.assertEquals(exitValue, 0, "PropertiesTest returned nonzero exit code.");
+ }
+
public static void main(String[] args) throws Exception {
System.out.println("testJAXPProperties started");
testJAXPProperties();
@@ -64,7 +139,8 @@
}
/*
- * Tests how does jaxp.properties affects the resolution.
+ * Tests how jaxp.properties affects the resolution.
+ * Be careful: This test modifies jaxp.properties in the used JDK.
*/
private static void testJAXPProperties() throws IOException {
generateJAXPProps(createJAXPPropsContent());
@@ -73,7 +149,7 @@
}
/*
- * Tests how does system properties affects the resolution.
+ * Tests how system properties affects the resolution.
*/
private static void testSystemProperties() {
setSystemProperties();
@@ -104,7 +180,7 @@
// The properties in jaxp.properties don't use default values
private static String createJAXPPropsContent() {
Map<String, String> props = new HashMap<>();
- props.put(FEATURE_FILES, getCatalogPath(CATALOG_PROPERTIES));
+ props.put(FEATURE_FILES, getCatalogPath(CATALOG_PROPERTIES).toString());
props.put(FEATURE_DEFER, DEFER_FALSE);
props.put(FEATURE_PREFER, PREFER_SYSTEM);
props.put(FEATURE_RESOLVE, RESOLVE_CONTINUE);
@@ -113,7 +189,7 @@
// The system properties don't use default values
private static void setSystemProperties() {
- System.setProperty(FEATURE_FILES, getCatalogPath(CATALOG_PROPERTIES));
+ System.setProperty(FEATURE_FILES, getCatalogPath(CATALOG_PROPERTIES).toString());
System.setProperty(FEATURE_DEFER, DEFER_FALSE);
System.setProperty(FEATURE_PREFER, PREFER_SYSTEM);
System.setProperty(FEATURE_RESOLVE, RESOLVE_CONTINUE);
--- a/jaxp/test/javax/xml/jaxp/isolatedjdk/catalog/PropertiesTest.sh Thu Jan 26 19:22:38 2017 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-#!/bin/sh
-
-# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
-# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-#
-# This code is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License version 2 only, as
-# published by the Free Software Foundation.
-#
-# This code is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# version 2 for more details (a copy is included in the LICENSE file that
-# accompanied this code).
-#
-# You should have received a copy of the GNU General Public License version
-# 2 along with this work; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-# or visit www.oracle.com if you need additional information or have any
-# questions.
-
-# @test
-# @bug 8077931
-# @summary This case tests if the properties FILES, DEFER, PREFER, RESOLVE in
-# jaxp.properties and system properties could be used.
-# @key intermittent
-# @library ../../libs/
-# @build catalog.CatalogTestUtils
-# @build PropertiesTest
-# @run shell/timeout=600 ../IsolatedJDK.sh JAXP_PROPS
-# @run shell/timeout=600 PropertiesTest.sh
-
-echo "Copies properties.xml to class path"
-TEST_CATALOG_PATH=${TESTCLASSES}/catalog/catalogFiles
-echo "TEST_CATALOG_PATH=${TEST_CATALOG_PATH}"
-mkdir -p ${TEST_CATALOG_PATH}
-cp ${TESTSRC}/catalogFiles/properties.xml ${TEST_CATALOG_PATH}/properties.xml
-
-# Execute test
-ISOLATED_JDK=./ISOLATED_JDK_JAXP_PROPS
-echo "Executes PropertiesTest"
-${ISOLATED_JDK}/bin/java -Dtest.src="${TESTSRC}/.." ${TESTVMOPTS} -cp "${TESTCLASSPATH}" catalog.PropertiesTest
-exitCode=$?
-
-# Cleanup ISOLATED_JDK
-rm -rf ${ISOLATED_JDK}
-
-# Results
-echo ''
-if [ $exitCode -gt 0 ]; then
- echo "PropertiesTest failed";
-else
- echo "PropertiesTest passed";
-fi
-exit $exitCode
-
--- a/jaxp/test/javax/xml/jaxp/libs/catalog/CatalogTestUtils.java Thu Jan 26 19:22:38 2017 +0000
+++ b/jaxp/test/javax/xml/jaxp/libs/catalog/CatalogTestUtils.java Thu Jan 26 21:20:30 2017 +0000
@@ -69,12 +69,6 @@
private static final String JAXP_PROPS = "jaxp.properties";
private static final String JAXP_PROPS_BAK = JAXP_PROPS + ".bak";
- /*
- * Force using slash as File separator as we always use cygwin to test in
- * Windows platform.
- */
- private static final String FILE_SEP = "/";
-
private CatalogTestUtils() { }
/* ********** create resolver ********** */
@@ -133,11 +127,14 @@
/* ********** jaxp.properties ********** */
/*
- * Generates the jaxp.properties with the specified content.
+ * Generates jaxp.properties with the specified content,
+ * takes a backup if possible.
*/
static void generateJAXPProps(String content) throws IOException {
Path filePath = getJAXPPropsPath();
Path bakPath = filePath.resolveSibling(JAXP_PROPS_BAK);
+ System.out.println("Creating new file " + filePath +
+ ", saving old version to " + bakPath + ".");
if (Files.exists(filePath) && !Files.exists(bakPath)) {
Files.move(filePath, bakPath);
}
@@ -146,14 +143,16 @@
}
/*
- * Deletes the jaxp.properties.
+ * Deletes jaxp.properties, restoring backup if possible.
*/
static void deleteJAXPProps() throws IOException {
Path filePath = getJAXPPropsPath();
+ Path bakPath = filePath.resolveSibling(JAXP_PROPS_BAK);
+ System.out.println("Removing file " + filePath +
+ ", restoring old version from " + bakPath + ".");
Files.delete(filePath);
- Path bakFilePath = filePath.resolveSibling(JAXP_PROPS_BAK);
- if (Files.exists(bakFilePath)) {
- Files.move(bakFilePath, filePath);
+ if (Files.exists(bakPath)) {
+ Files.move(bakPath, filePath);
}
}
--- a/jaxp/test/javax/xml/jaxp/libs/jaxp/library/JAXPTestUtilities.java Thu Jan 26 19:22:38 2017 +0000
+++ b/jaxp/test/javax/xml/jaxp/libs/jaxp/library/JAXPTestUtilities.java Thu Jan 26 21:20:30 2017 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -85,13 +85,13 @@
* A map storing every test's current test file pointer. File number should
* be incremental and it's a thread-safe reading on this file number.
*/
- private static final ConcurrentHashMap<Class, Integer> currentFileNumber
+ private static final ConcurrentHashMap<Class<?>, Integer> currentFileNumber
= new ConcurrentHashMap<>();
/**
* BOM table for storing BOM header.
*/
- private final static Map<String, byte[]> bom = new HashMap();
+ private final static Map<String, byte[]> bom = new HashMap<>();
/**
* Initialize all BOM headers.
@@ -313,7 +313,7 @@
* @param clazz test class.
* @return next test output file name.
*/
- public static String getNextFile(Class clazz) {
+ public static String getNextFile(Class<?> clazz) {
int nextNumber = currentFileNumber.contains(clazz)
? currentFileNumber.get(clazz) + 1 : 1;
Integer i = currentFileNumber.putIfAbsent(clazz, nextNumber);
@@ -332,7 +332,7 @@
* path.
* @return a string represents the full path of accessing path.
*/
- public static String getPathByClassName(Class clazz, String relativeDir) {
+ public static String getPathByClassName(Class<?> clazz, String relativeDir) {
String javaSourcePath = System.getProperty("test.src").replaceAll("\\" + File.separator, FILE_SEP);
String normalizedPath = Paths.get(javaSourcePath, relativeDir).normalize().
toAbsolutePath().toString();
@@ -435,7 +435,7 @@
*/
public static void runWithTmpPermission(Runnable r, Permission... ps) {
JAXPPolicyManager policyManager = JAXPPolicyManager.getJAXPPolicyManager(false);
- List<Integer> tmpPermissionIndexes = new ArrayList();
+ List<Integer> tmpPermissionIndexes = new ArrayList<>();
if (policyManager != null) {
for (Permission p : ps)
tmpPermissionIndexes.add(policyManager.addTmpPermission(p));
@@ -459,7 +459,7 @@
*/
public static <T> T runWithTmpPermission(Supplier<T> s, Permission... ps) {
JAXPPolicyManager policyManager = JAXPPolicyManager.getJAXPPolicyManager(false);
- List<Integer> tmpPermissionIndexes = new ArrayList();
+ List<Integer> tmpPermissionIndexes = new ArrayList<>();
if (policyManager != null) {
for (Permission p : ps)
tmpPermissionIndexes.add(policyManager.addTmpPermission(p));
@@ -483,7 +483,7 @@
*/
public static void tryRunWithTmpPermission(RunnableWithException r, Permission... ps) throws Exception {
JAXPPolicyManager policyManager = JAXPPolicyManager.getJAXPPolicyManager(false);
- List<Integer> tmpPermissionIndexes = new ArrayList();
+ List<Integer> tmpPermissionIndexes = new ArrayList<>();
if (policyManager != null) {
for (Permission p : ps)
tmpPermissionIndexes.add(policyManager.addTmpPermission(p));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jaxp/test/javax/xml/jaxp/unittest/stream/EventsTest/EventFilterSupportTest.java Thu Jan 26 21:20:30 2017 +0000
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package stream.EventsTest;
+
+import java.io.IOException;
+import java.io.InputStream;
+import javax.xml.stream.EventFilter;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.XMLEvent;
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertEquals;
+
+/**
+ * @test
+ * @bug 8173111
+ * @summary tests that filtering out nested elements doesn't end up in
+ * a StackOverflowException
+ * @run testng/othervm stream.EventsTest.EventFilterSupportTest
+ * @author danielfuchs
+ */
+public class EventFilterSupportTest {
+ static final String ROOT = "xml";
+ static final String NEXT = "foo";
+ static final String SMOKE = "<?xml version=\"1.0\" encoding=\"US-ASCII\"?>"
+ + "<xml><foo><foo><foo></foo></foo></foo></xml>";
+ // A number high enough to trigger StackOverflowException before the fix.
+ static final int MAX = 100_000;
+
+ public static void main(String[] args)
+ throws XMLStreamException, IOException {
+ smokeTest();
+ testNextEvent(MAX);
+ testNextTag(MAX);
+ System.out.println("Tests passed...");
+ }
+
+ // The smoke test just verifies that our TestInputStream works as
+ // expected and produces the expected stream of characters.
+ // Here we test it with 4 nested elements.
+ @Test
+ public static void smokeTest() throws IOException {
+ System.out.println("\nSmoke test...");
+ StringBuilder sb = new StringBuilder();
+ try (InputStream ts = new TestInputStream(4)) {
+ int c;
+ while ((c = ts.read()) != -1) {
+ System.out.print((char)c);
+ sb.append((char)c);
+ }
+ }
+ assertEquals(sb.toString(), SMOKE, "Smoke test failed");
+ System.out.println("\nSmoke test passed\n");
+ }
+
+ // Test calling XMLEventReader.nextEvent()
+ @Test
+ public static void testNextEvent() throws IOException, XMLStreamException {
+ testNextEvent(MAX);
+ }
+
+ // Without the fix, will cause a StackOverflowException if 'max' is high
+ // enough
+ private static void testNextEvent(int max)
+ throws IOException, XMLStreamException {
+ System.out.println("\nTest nextEvent (" + max + ")...");
+ XMLEventReader reader = createXmlReader(max);
+ XMLEvent event;
+ do {
+ event = reader.nextEvent();
+ System.out.println(event);
+ } while (event.getEventType() != XMLEvent.END_DOCUMENT);
+ System.out.println("nextEvent passed\n");
+ }
+
+ // Test calling XMLEventReader.nextTag()
+ @Test
+ public static void testNextTag() throws IOException, XMLStreamException {
+ testNextTag(MAX);
+ }
+
+ // Without the fix, will cause a StackOverflowException if 'max' is high
+ // enough
+ private static void testNextTag(int max)
+ throws IOException, XMLStreamException {
+ System.out.println("\nTest nextTag (" + max + ")...");
+ XMLEventReader reader = createXmlReader(max);
+ XMLEvent event;
+ do {
+ event = reader.nextTag();
+ System.out.println(event);
+ if (event.getEventType() == XMLEvent.END_ELEMENT
+ && event.asEndElement().getName().getLocalPart().equals(ROOT)) {
+ break;
+ }
+ } while (true);
+ System.out.println("nextTag passed\n");
+ }
+
+ private static XMLEventReader createXmlReader(int max)
+ throws XMLStreamException {
+ TestInputStream ts = new TestInputStream(max);
+ XMLInputFactory xif = XMLInputFactory.newInstance();
+ XMLEventReader reader = xif.createXMLEventReader(ts);
+ return xif.createFilteredReader(reader, new TagFilter(max));
+ }
+
+ // An input stream that pretends to contain 'max - 1' nested NEXT tags
+ // within a ROOT element:
+ // <?xml version="1.0" encoding="US-ASCII"?>
+ // <ROOT><NEXT><NEXT>...</NEXT></NEXT></ROOT>
+ // (1 ROOT element + max-1 nested NEXT elements)
+ public static class TestInputStream extends InputStream {
+
+ int open = 0;
+ int i = 0;
+ int n = 0;
+ final int max;
+
+ public TestInputStream(int max) {
+ this.max = max;
+ }
+
+ String tag() {
+ if (n == 0) {
+ // opening first element - includes the XML processing instruction.
+ return "?xml version=\"1.0\" encoding=\"US-ASCII\"?><" + ROOT;
+ }
+ if (n == 2 * max -1) {
+ // closing the first element
+ // we have 'max' opening tags (0..max-1) followed by
+ // 'max' closing tags (max..2*max-1)
+ // for n in [0..max-1] we open the tags,
+ // for n in [max..2*max-1] we close them (in reverse order)
+ return ROOT;
+ }
+ // everything between [1..max-2] is a NEXT element tag (opening
+ // or closing)
+ return NEXT;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (n >= 2 * max) return -1;
+ if (open == 0) {
+ open = 1;
+ return '<';
+ }
+ if (open == 1 && n >= max) {
+ // we have opened the ROOT element + n-1 nested NEXT elements,
+ // so now we need to start closing them all in reverse order.
+ open = 2;
+ return '/';
+ }
+ String tag = tag();
+ if (open > 0 && i < tag.length()) {
+ return tag.charAt(i++);
+ }
+ if (open > 0 && i == tag.length()) {
+ open = 0; i = 0; n++;
+ return '>';
+ }
+ return -1;
+ }
+ }
+
+ public static final class TagFilter implements EventFilter {
+ int count;
+ final int max;
+
+ public TagFilter(int max) {
+ this.max = max;
+ }
+
+ // Filters everything except the ROOT element.
+ @Override
+ public boolean accept(XMLEvent event) {
+ int type = event.getEventType();
+ if (type == XMLEvent.START_ELEMENT) {
+ String loc = event.asStartElement().getName().getLocalPart();
+ if (count == 0 || count == 1) System.out.println("<" + loc + ">");
+ count++;
+ return ROOT.equals(loc);
+ }
+ if (type == XMLEvent.END_ELEMENT) {
+ if (count == max) System.out.println("Got " + count + " elements");
+ String loc = event.asEndElement().getName().getLocalPart();
+ count--;
+ if (count == 0 || count == 1) System.out.println("</" + loc + ">");
+ return ROOT.equals(loc);
+ }
+ if (type == XMLEvent.PROCESSING_INSTRUCTION) return true;
+ if (type == XMLEvent.START_DOCUMENT) return true;
+ if (type == XMLEvent.END_DOCUMENT) return true;
+ return false;
+ }
+ }
+
+}